/*
This file is part of jpcsp.
Jpcsp is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Jpcsp is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Jpcsp. If not, see <http://www.gnu.org/licenses/>.
*/
package jpcsp.HLE.modules;
import static jpcsp.HLE.kernel.types.SceKernelErrors.ERROR_ERRNO_BASE;
import static jpcsp.HLE.modules.sceNetAdhocctl.fillNextPointersInLinkedList;
import jpcsp.HLE.BufferInfo;
import jpcsp.HLE.BufferInfo.LengthInfo;
import jpcsp.HLE.BufferInfo.Usage;
import jpcsp.HLE.CanBeNull;
import jpcsp.HLE.CheckArgument;
import jpcsp.HLE.DebugMemory;
import jpcsp.HLE.HLEFunction;
import jpcsp.HLE.HLEModule;
import jpcsp.HLE.HLEUnimplemented;
import jpcsp.HLE.PspString;
import jpcsp.HLE.SceKernelErrorException;
import jpcsp.HLE.TPointer;
import jpcsp.HLE.TPointer32;
import java.io.IOException;
import java.io.InterruptedIOException;
import java.net.BindException;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.SocketAddress;
import java.net.SocketException;
import java.net.UnknownHostException;
import java.nio.ByteBuffer;
import java.nio.channels.AsynchronousCloseException;
import java.nio.channels.CancelledKeyException;
import java.nio.channels.ClosedByInterruptException;
import java.nio.channels.ClosedChannelException;
import java.nio.channels.DatagramChannel;
import java.nio.channels.NotYetConnectedException;
import java.nio.channels.SelectableChannel;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedList;
import org.apache.log4j.Logger;
import com.savarese.rocksaw.net.RawSocket;
import jpcsp.HLE.Modules;
import jpcsp.HLE.kernel.managers.SceUidManager;
import jpcsp.HLE.kernel.types.IAction;
import jpcsp.HLE.kernel.types.SceKernelErrors;
import jpcsp.HLE.kernel.types.SceKernelThreadInfo;
import jpcsp.HLE.kernel.types.SceNetInetTcpcbstat;
import jpcsp.HLE.kernel.types.pspAbstractMemoryMappedStructure;
import jpcsp.HLE.kernel.types.pspNetSockAddrInternet;
import jpcsp.memory.IMemoryReader;
import jpcsp.memory.IMemoryWriter;
import jpcsp.memory.MemoryReader;
import jpcsp.memory.MemoryWriter;
import jpcsp.network.RawChannel;
import jpcsp.network.RawSelector;
import jpcsp.remote.HTTPConfiguration;
import jpcsp.remote.HTTPConfiguration.HttpServerConfiguration;
import jpcsp.remote.HTTPServer;
import jpcsp.settings.AbstractStringSettingsListener;
import jpcsp.settings.Settings;
import jpcsp.util.Utilities;
import jpcsp.Emulator;
import jpcsp.Memory;
public class sceNetInet extends HLEModule {
public static Logger log = Modules.getLogger("sceNetInet");
public static final int AF_INET = 2; // Address familiy internet
public static final int SOCK_STREAM = 1; // Stream socket
public static final int SOCK_DGRAM = 2; // Datagram socket
public static final int SOCK_RAW = 3; // Raw socket
public static final int SOCK_STREAM_UNKNOWN_10 = 10; // Looks like a SOCK_STREAM, but specifics unknown
public static final int SOCK_DGRAM_UNKNOWN_6 = 6; // Looks like a SOCK_DGRAM, but specifics unknown
private static final String[] socketTypeNames = new String[] {
"Unknown0", "SOCK_STREAM", "SOCK_DGRAM", "SOCK_RAW", "Unknown4", "Unknown5", "SOCK_DGRAM_UNKNOWN_6", "Unknown7", "Unknown8", "Unknown9", "SOCK_STREAM_UNKNOWN_10"
};
public static final int SOL_SOCKET = 0xFFFF; // Socket level
public static final int INADDR_ANY = 0x00000000; // wildcard/any IP address
public static final int INADDR_BROADCAST = 0xFFFFFFFF; // Broadcast address
public static final int EAGAIN = SceKernelErrors.ERROR_ERRNO_RESOURCE_UNAVAILABLE & 0x0000FFFF;
public static final int EWOULDBLOCK = EAGAIN; // EWOULDBLOCK == EAGAIN
public static final int EINPROGRESS = SceKernelErrors.ERROR_ERRNO_IN_PROGRESS & 0x0000FFFF;
public static final int ENOTCONN = SceKernelErrors.ERROR_ERRNO_NOT_CONNECTED & 0x0000FFFF;
public static final int ECLOSED = SceKernelErrors.ERROR_ERRNO_CLOSED & 0x0000FFFF;
public static final int EIO = SceKernelErrors.ERROR_ERRNO_IO_ERROR & 0x0000FFFF;
public static final int EISCONN = SceKernelErrors.ERROR_ERRNO_IS_ALREADY_CONNECTED & 0x0000FFFF;
public static final int EALREADY = SceKernelErrors.ERROR_ERRNO_ALREADY & 0x0000FFFF;
public static final int EADDRNOTAVAIL = SceKernelErrors.ERROR_ERRNO_ADDRESS_NOT_AVAILABLE & 0x0000FFFF;
// Types of socket shutdown ("how" parameter)
public static final int SHUT_RD = 0; // Disallow further receives
public static final int SHUT_WR = 1; // Disallow further sends
public static final int SHUT_RDWR = 2; // Disallow further sends/receives
// Socket options
public static final int SO_DEBUG = 0x0001; // turn on debugging info recording
public static final int SO_ACCEPTCONN = 0x0002; // socket has had listen()
public static final int SO_REUSEADDR = 0x0004; // allow local address reuse
public static final int SO_KEEPALIVE = 0x0008; // keep connections alive
public static final int SO_DONTROUTE = 0x0010; // just use interface addresses
public static final int SO_BROADCAST = 0x0020; // permit sending of broadcast msgs
public static final int SO_USELOOPBACK = 0x0040; // bypass hardware when possible
public static final int SO_LINGER = 0x0080; // linger on close if data present
public static final int SO_OOBINLINE = 0x0100; // leave received OOB data in line
public static final int SO_REUSEPORT = 0x0200; // allow local address & port reuse
public static final int SO_TIMESTAMP = 0x0400; // timestamp received dgram traffic
public static final int SO_ONESBCAST = 0x0800; // allow broadcast to 255.255.255.255
public static final int SO_SNDBUF = 0x1001; // send buffer size
public static final int SO_RCVBUF = 0x1002; // receive buffer size
public static final int SO_SNDLOWAT = 0x1003; // send low-water mark
public static final int SO_RCVLOWAT = 0x1004; // receive low-water mark
public static final int SO_SNDTIMEO = 0x1005; // send timeout
public static final int SO_RCVTIMEO = 0x1006; // receive timeout
public static final int SO_ERROR = 0x1007; // get error status and clear
public static final int SO_TYPE = 0x1008; // get socket type
public static final int SO_OVERFLOWED = 0x1009; // datagrams: return packets dropped
public static final int SO_NONBLOCK = 0x1009; // non-blocking I/O
// Bitmasks for sceNetInetPoll()
public static final int POLLIN = 0x0001;
public static final int POLLPRI = 0x0002;
public static final int POLLOUT = 0x0004;
public static final int POLLERR = 0x0008;
public static final int POLLHUP = 0x0010;
public static final int POLLNVAL = 0x0020;
public static final int POLLRDNORM = 0x0040;
public static final int POLLRDBAND = 0x0080;
public static final int POLLWRBAND = 0x0100;
// Infinite timeout for scenetInetPoll()
public static final int POLL_INFTIM = -1;
// Polling period (micro seconds) for blocking operations
protected static final int BLOCKED_OPERATION_POLLING_MICROS = 10000;
protected static final int readSelectionKeyOperations = SelectionKey.OP_READ | SelectionKey.OP_ACCEPT;
protected static final int writeSelectionKeyOperations = SelectionKey.OP_WRITE;
// MSG flag options for sceNetInetRecvfrom and sceNetInetSendto (from <sys/socket.h>)
public static final int MSG_OOB = 0x1; // Requests out-of-band data.
public static final int MSG_PEEK = 0x2; // Peeks at an incoming message.
public static final int MSG_DONTROUTE = 0x4; // Sends data without routing tables.
public static final int MSG_EOR = 0x8; // Terminates a record.
public static final int MSG_TRUNC = 0x10; // Truncates data before receiving it.
public static final int MSG_CTRUNC = 0x20; // Truncates control data before receiving it.
public static final int MSG_WAITALL = 0x40; // Waits until all data can be returned (blocking).
public static final int MSG_DONTWAIT = 0x80; // Doesn't wait until all data can be returned (non-blocking).
public static final int MSG_BCAST = 0x100; // Message received by link-level broadcast.
public static final int MSG_MCAST = 0x200; // Message received by link-level multicast.
// TCP FSM state definitions. Per RFC793, September, 1981.
public static final int TCPS_CLOSED = 0; // closed
public static final int TCPS_LISTEN = 1; // listening for connection
public static final int TCPS_SYN_SENT = 2; // active, have sent syn
public static final int TCPS_SYN_RECEIVED = 3; // have send and received syn
public static final int TCPS_ESTABLISHED = 4; // established
public static final int TCPS_CLOSE_WAIT = 5; // rcvd fin, waiting for close
public static final int TCPS_FIN_WAIT_1 = 6; // have closed, sent fin
public static final int TCPS_CLOSING = 7; // closed xchd FIN; await FIN ACK
public static final int TCPS_LAST_ACK = 8; // had fin and close; await FIN ACK
public static final int TCPS_FIN_WAIT_2 = 9; // have closed, fin is acked
public static final int TCPS_TIME_WAIT = 10; // in 2*msl quiet wait after close
private static InetAddress[] broadcastAddresses;
private static class BroadcastAddressSettingsListener extends AbstractStringSettingsListener {
@Override
protected void settingsValueChanged(String value) {
// Force a new evaluation of broadcasrAddresses in getBroadcastInetSocketAddress()
broadcastAddresses = null;
}
}
private class AsyncStartThread extends Thread {
@Override
public void run() {
// These DNS requests take some time to complete (1-2 seconds),
// this is why we execute them in a separate thread.
for (HttpServerConfiguration doProxyServer : HTTPConfiguration.doProxyServers) {
try {
InetAddress inetAddresses[] = (InetAddress[]) InetAddress.getAllByName(doProxyServer.serverName);
doProxyInetAddresses = Utilities.merge(doProxyInetAddresses, inetAddresses);
} catch (UnknownHostException e) {
if (log.isDebugEnabled()) {
log.debug(String.format("sceNetInet cannot resolve '%s': %s", doProxyServer, e.toString()));
}
}
}
}
}
public static InetSocketAddress[] getBroadcastInetSocketAddress(int port) throws UnknownHostException {
if (broadcastAddresses == null) {
String broadcastAddressNames = Settings.getInstance().readString("network.broadcastAddress");
if (broadcastAddressNames != null && broadcastAddressNames.length() > 0) {
String [] addressNames = broadcastAddressNames.split(" *[,;] *");
ArrayList<InetAddress> addresses = new ArrayList<InetAddress>();
for (int i = 0; i < addressNames.length; i++) {
try {
InetAddress address = InetAddress.getByName(addressNames[i]);
addresses.add(address);
} catch (Exception e) {
log.error(String.format("Error resolving the broadcast address '%s' from the Settings file", addressNames[i]), e);
}
}
if (addresses.size() > 0) {
broadcastAddresses = addresses.toArray(new InetAddress[addresses.size()]);
}
}
if (broadcastAddresses == null) {
// When SO_ONESBCAST is not enabled, map the broadcast address
// to the broadcast address from the network of the local IP address.
// E.g.
// - localHostIP: A.B.C.D
// - subnetMask: 255.255.255.0
// -> localBroadcastIP: A.B.C.255
InetAddress localInetAddress = InetAddress.getByName(sceNetApctl.getLocalHostIP());
int localAddress = bytesToInternetAddress(localInetAddress.getAddress());
int subnetMask = Integer.reverseBytes(sceNetApctl.getSubnetMaskInt());
int localBroadcastAddressInt = localAddress & subnetMask;
localBroadcastAddressInt |= INADDR_BROADCAST & ~subnetMask;
broadcastAddresses = new InetAddress[1];
broadcastAddresses[0] = InetAddress.getByAddress(internetAddressToBytes(localBroadcastAddressInt));
}
if (log.isDebugEnabled()) {
for (int i = 0; i < broadcastAddresses.length; i++) {
log.debug(String.format("Using the following broadcast address#%d: %s", i + 1, broadcastAddresses[i].getHostAddress()));
}
}
}
InetSocketAddress[] socketAddresses = new InetSocketAddress[broadcastAddresses.length];
for (int i = 0; i < socketAddresses.length; i++) {
socketAddresses[i] = new InetSocketAddress(broadcastAddresses[i], port);
}
return socketAddresses;
}
protected static abstract class BlockingState implements IAction {
public pspInetSocket inetSocket;
public int threadId;
public boolean threadBlocked;
public long timeout; // microseconds
public long start; // Clock.microTime
private boolean insideExecute;
public BlockingState(pspInetSocket inetSocket, long timeout) {
this.inetSocket = inetSocket;
threadId = Modules.ThreadManForUserModule.getCurrentThreadID();
threadBlocked = false;
start = Emulator.getClock().microTime();
this.timeout = timeout;
}
public boolean isTimeout() {
long now = Emulator.getClock().microTime();
return now >= start + timeout;
}
@Override
public void execute() {
// Avoid executing the blocking state while already processing it.
// E.g. when the thread is unblocked by executeBlockingState(),
// this action is called again
if (!insideExecute) {
insideExecute = true;
executeBlockingState();
insideExecute = false;
}
}
protected abstract void executeBlockingState();
}
protected static class BlockingAcceptState extends BlockingState {
public pspNetSockAddrInternet acceptAddr;
public BlockingAcceptState(pspInetSocket inetSocket, pspNetSockAddrInternet acceptAddr) {
super(inetSocket, pspInetSocket.NO_TIMEOUT);
this.acceptAddr = acceptAddr;
}
@Override
protected void executeBlockingState() {
inetSocket.blockedAccept(this);
}
}
protected static class BlockingPollState extends BlockingState {
public Selector selector;
public pspInetPollFd[] pollFds;
public BlockingPollState(Selector selector, pspInetPollFd[] pollFds, long timeout) {
super(null, timeout);
this.selector = selector;
this.pollFds = pollFds;
}
@Override
protected void executeBlockingState() {
Modules.sceNetInetModule.blockedPoll(this);
}
}
protected static class BlockingSelectState extends BlockingState {
public Selector selector;
public RawSelector rawSelector;
public int numberSockets;
public TPointer readSocketsAddr;
public TPointer writeSocketsAddr;
public TPointer outOfBandSocketsAddr;
public int count;
public BlockingSelectState(Selector selector, RawSelector rawSelector, int numberSockets, TPointer readSocketsAddr, TPointer writeSocketsAddr, TPointer outOfBandSocketsAddr, long timeout, int count) {
super(null, timeout);
this.selector = selector;
this.rawSelector = rawSelector;
this.numberSockets = numberSockets;
this.readSocketsAddr = readSocketsAddr;
this.writeSocketsAddr = writeSocketsAddr;
this.outOfBandSocketsAddr = outOfBandSocketsAddr;
this.count = count;
}
@Override
protected void executeBlockingState() {
Modules.sceNetInetModule.blockedSelect(this);
}
}
protected static class BlockingReceiveState extends BlockingState {
public int buffer;
public int bufferLength;
public int flags;
public int receivedLength;
public BlockingReceiveState(pspInetSocket inetSocket, int buffer, int bufferLength, int flags, int receivedLength) {
super(inetSocket, inetSocket.getReceiveTimeout());
this.buffer = buffer;
this.bufferLength = bufferLength;
this.flags = flags;
this.receivedLength = receivedLength;
}
@Override
protected void executeBlockingState() {
inetSocket.blockedRecv(this);
}
}
protected static class BlockingReceiveFromState extends BlockingState {
public int buffer;
public int bufferLength;
public int flags;
public pspNetSockAddrInternet fromAddr;
public int receivedLength;
public BlockingReceiveFromState(pspInetSocket inetSocket, int buffer, int bufferLength, int flags, pspNetSockAddrInternet fromAddr, int receivedLength) {
super(inetSocket, inetSocket.getReceiveTimeout());
this.buffer = buffer;
this.bufferLength = bufferLength;
this.flags = flags;
this.fromAddr = fromAddr;
this.receivedLength = receivedLength;
}
@Override
protected void executeBlockingState() {
inetSocket.blockedRecvfrom(this);
}
}
protected static class BlockingSendState extends BlockingState {
public int buffer;
public int bufferLength;
public int flags;
public int sentLength;
public BlockingSendState(pspInetSocket inetSocket, int buffer, int bufferLength, int flags, int sentLength) {
super(inetSocket, inetSocket.getSendTimeout());
this.buffer = buffer;
this.bufferLength = bufferLength;
this.flags = flags;
this.sentLength = sentLength;
}
@Override
protected void executeBlockingState() {
inetSocket.blockedSend(this);
}
}
protected static class BlockingSendToState extends BlockingState {
public int buffer;
public int bufferLength;
public int flags;
public pspNetSockAddrInternet toAddr;
public int sentLength;
public BlockingSendToState(pspInetSocket inetSocket, int buffer, int bufferLength, int flags, pspNetSockAddrInternet toAddr, int sentLength) {
super(inetSocket, inetSocket.getSendTimeout());
this.buffer = buffer;
this.bufferLength = bufferLength;
this.flags = flags;
this.toAddr = toAddr;
this.sentLength = sentLength;
}
@Override
protected void executeBlockingState() {
inetSocket.blockedSendto(this);
}
}
protected abstract class pspInetSocket {
public static final long NO_TIMEOUT = Integer.MAX_VALUE * 1000000L;
public static final int NO_TIMEOUT_INT = Integer.MAX_VALUE;
private int uid;
protected boolean blocking = true;
protected boolean broadcast;
protected boolean onesBroadcast;
protected int receiveLowWaterMark = 1;
protected int sendLowWaterMark = 2048;
protected int receiveTimeout = NO_TIMEOUT_INT;
protected int sendTimeout = NO_TIMEOUT_INT;
protected int receiveBufferSize = 0x4000;
protected int sendBufferSize = 0x4000;
protected int error;
protected boolean reuseAddress;
protected boolean keepAlive;
protected boolean lingerEnabled;
protected int linger;
protected boolean tcpNoDelay;
private pspNetSockAddrInternet localAddr;
private pspNetSockAddrInternet remoteAddr;
public pspInetSocket(int uid) {
this.uid = uid;
}
public int getUid() {
return uid;
}
public abstract int connect(pspNetSockAddrInternet addr);
public abstract int bind(pspNetSockAddrInternet addr);
public abstract int recv(int buffer, int bufferLength, int flags, BlockingReceiveState blockingState);
public abstract int send(int buffer, int bufferLength, int flags, BlockingSendState blockingState);
public abstract int recvfrom(int buffer, int bufferLength, int flags, pspNetSockAddrInternet fromAddr, BlockingReceiveFromState blockingState);
public abstract int sendto(int buffer, int bufferLength, int flags, pspNetSockAddrInternet toAddr, BlockingSendToState blockingState);
public abstract int close();
public abstract SelectableChannel getSelectableChannel();
public abstract boolean isValid();
public abstract int getSockname(pspNetSockAddrInternet sockAddrInternet);
public abstract int getPeername(pspNetSockAddrInternet sockAddrInternet);
public abstract int shutdown(int how);
public abstract int listen(int backlog);
public abstract int accept(pspNetSockAddrInternet sockAddrInternet, BlockingAcceptState blockingState);
public abstract boolean finishConnect();
public int setBlocking(boolean blocking) {
this.blocking = blocking;
return 0;
}
public boolean isBlocking() {
return blocking;
}
public boolean isBlocking(int flags) {
boolean blocking;
// Flag MSG_DONTWAIT set: the IO operation is non-blocking
if ((flags & MSG_DONTWAIT) != 0) {
blocking = false;
} else {
blocking = isBlocking();
}
if (log.isDebugEnabled()) {
log.debug(String.format("isBlocking(0x%X)=%b", flags, blocking));
}
return blocking;
}
protected SocketAddress getSocketAddress(int address, int port) throws UnknownHostException {
SocketAddress socketAddress;
if (address == INADDR_ANY) {
socketAddress = new InetSocketAddress(port);
} else if (address == INADDR_BROADCAST && !isOnesBroadcast()) {
socketAddress = getBroadcastInetSocketAddress(port)[0];
} else {
socketAddress = new InetSocketAddress(InetAddress.getByAddress(internetAddressToBytes(address)), port);
}
return socketAddress;
}
protected SocketAddress[] getMultiSocketAddress(int address, int port) throws UnknownHostException {
SocketAddress[] socketAddress;
if (address == INADDR_ANY) {
socketAddress = new SocketAddress[1];
socketAddress[0] = new InetSocketAddress(port);
} else if (address == INADDR_BROADCAST && !isOnesBroadcast()) {
socketAddress = getBroadcastInetSocketAddress(port);
} else {
socketAddress = new SocketAddress[1];
socketAddress[0] = new InetSocketAddress(InetAddress.getByAddress(internetAddressToBytes(address)), port);
}
return socketAddress;
}
protected SocketAddress getSocketAddress(pspNetSockAddrInternet addr) throws UnknownHostException {
return getSocketAddress(addr.sin_addr, addr.sin_port);
}
protected pspNetSockAddrInternet getNetSockAddrInternet(SocketAddress socketAddress) {
pspNetSockAddrInternet addr = null;
if (socketAddress != null) {
if (socketAddress instanceof InetSocketAddress) {
addr = new pspNetSockAddrInternet();
addr.readFromInetSocketAddress((InetSocketAddress) socketAddress);
}
}
return addr;
}
protected SocketAddress[] getMultiSocketAddress(pspNetSockAddrInternet addr) throws UnknownHostException {
return getMultiSocketAddress(addr.sin_addr, addr.sin_port);
}
protected InetAddress getInetAddress(int address) throws UnknownHostException {
InetAddress inetAddress;
inetAddress = InetAddress.getByAddress(internetAddressToBytes(address));
return inetAddress;
}
protected InetAddress getInetAddress(pspNetSockAddrInternet addr) throws UnknownHostException {
return getInetAddress(addr.sin_addr);
}
protected void copySocketAttributes(pspInetSocket from) {
setBlocking(from.isBlocking());
setBroadcast(from.isBroadcast());
setOnesBroadcast(from.isOnesBroadcast());
setReceiveLowWaterMark(from.getReceiveLowWaterMark());
setSendLowWaterMark(from.getSendLowWaterMark());
setReceiveTimeout(from.getReceiveTimeout());
setSendTimeout(from.getSendTimeout());
setReceiveBufferSize(from.getReceiveBufferSize());
setSendBufferSize(from.getSendBufferSize());
setReuseAddress(from.isReuseAddress());
setKeepAlive(from.isKeepAlive());
setLinger(from.isLingerEnabled(), from.getLinger());
setTcpNoDelay(from.isTcpNoDelay());
}
public boolean isBroadcast() {
return broadcast;
}
public int setBroadcast(boolean broadcast) {
this.broadcast = broadcast;
return 0;
}
public int getReceiveLowWaterMark() {
return receiveLowWaterMark;
}
public void setReceiveLowWaterMark(int receiveLowWaterMark) {
this.receiveLowWaterMark = receiveLowWaterMark;
}
public int getSendLowWaterMark() {
return sendLowWaterMark;
}
public void setSendLowWaterMark(int sendLowWaterMark) {
this.sendLowWaterMark = sendLowWaterMark;
}
protected byte[] getByteArray(int address, int length) {
byte[] bytes = new byte[length];
IMemoryReader memoryReader = MemoryReader.getMemoryReader(address, length, 1);
for (int i = 0; i < length; i++) {
bytes[i] = (byte) memoryReader.readNext();
}
return bytes;
}
protected ByteBuffer getByteBuffer(int address, int length) {
return ByteBuffer.wrap(getByteArray(address, length));
}
@Override
public String toString() {
return String.format("pspInetSocket[uid=%d]", uid);
}
public boolean isOnesBroadcast() {
return onesBroadcast;
}
public void setOnesBroadcast(boolean onesBroadcast) {
this.onesBroadcast = onesBroadcast;
}
public int getReceiveTimeout() {
if (log.isDebugEnabled()) {
log.debug(String.format("receiveTimeout=%d", receiveTimeout));
}
return receiveTimeout;
}
public void setReceiveTimeout(int receiveTimeout) {
this.receiveTimeout = receiveTimeout;
}
public int getSendTimeout() {
return sendTimeout;
}
public void setSendTimeout(int sendTimeout) {
this.sendTimeout = sendTimeout;
}
public int getErrorAndClearToSelf() {
int value = error;
// clear error and errno
clearErrorToSelf();
return value;
}
protected void setSocketError(int error) {
this.error = error;
}
protected void setSocketError(Exception e) {
if (e instanceof NotYetConnectedException) {
setSocketError(ENOTCONN);
} else if (e instanceof ClosedChannelException) {
setSocketError(ECLOSED);
} else if (e instanceof AsynchronousCloseException) {
setSocketError(ECLOSED);
} else if (e instanceof ClosedByInterruptException) {
setSocketError(ECLOSED);
} else if (e instanceof BindException) {
setSocketError(EADDRNOTAVAIL);
} else if (e instanceof IOException) {
setSocketError(EIO);
} else {
setSocketError(-1); // Unknown error
}
}
protected void setError(IOException e, BlockingState blockingState) {
setSocketError(e);
setErrno(error, blockingState);
}
protected void setErrorToSelf(IOException e) {
setError(e, null);
}
protected void setError(int error, BlockingState blockingState) {
setSocketError(error);
setErrno(this.error, blockingState);
}
protected void setErrorToSelf(int error) {
setError(error, null);
}
protected void clearErrorToSelf() {
clearError(null);
}
protected void clearError(BlockingState blockingState) {
error = 0;
setErrno(0, blockingState);
}
public int getReceiveBufferSize() {
return receiveBufferSize;
}
public int setReceiveBufferSize(int receiveBufferSize) {
this.receiveBufferSize = receiveBufferSize;
return 0;
}
public int getSendBufferSize() {
return sendBufferSize;
}
public int setSendBufferSize(int sendBufferSize) {
this.sendBufferSize = sendBufferSize;
return 0;
}
public boolean isReuseAddress() {
return reuseAddress;
}
public int setReuseAddress(boolean reuseAddress) {
this.reuseAddress = reuseAddress;
return 0;
}
public boolean isKeepAlive() {
return keepAlive;
}
public int setKeepAlive(boolean keepAlive) {
this.keepAlive = keepAlive;
return 0;
}
public boolean isLingerEnabled() {
return lingerEnabled;
}
public int getLinger() {
return linger;
}
public int setLinger(boolean enabled, int linger) {
this.lingerEnabled = enabled;
this.linger = linger;
return 0;
}
public boolean isTcpNoDelay() {
return tcpNoDelay;
}
public int setTcpNoDelay(boolean tcpNoDelay) {
this.tcpNoDelay = tcpNoDelay;
return 0;
}
protected void storeBytes(int address, int length, byte[] bytes) {
if (length > 0) {
IMemoryWriter memoryWriter = MemoryWriter.getMemoryWriter(address, length, 1);
for (int i = 0; i < length; i++) {
memoryWriter.writeNext(bytes[i]);
}
memoryWriter.flush();
}
}
public Selector getSelector(Selector selector, RawSelector rawSelector) {
return selector;
}
public int recv(int buffer, int bufferLength, int flags) {
if ((flags & ~MSG_DONTWAIT) != 0) {
log.warn(String.format("sceNetInetRecv unsupported flag 0x%X on socket", flags));
}
return recv(buffer, bufferLength, flags, null);
}
public void blockedRecv(BlockingReceiveState blockingState) {
if (blockingState.isTimeout()) {
if (log.isDebugEnabled()) {
log.debug(String.format("sceNetInetRecv socket=0x%X returning %d (timeout)", getUid(), blockingState.receivedLength));
}
setErrno(EAGAIN, blockingState);
unblockThread(blockingState, blockingState.receivedLength);
} else {
int length = recv(blockingState.buffer + blockingState.receivedLength, blockingState.bufferLength - blockingState.receivedLength, blockingState.flags, blockingState);
if (length >= 0) {
unblockThread(blockingState, blockingState.receivedLength);
}
}
}
public int send(int buffer, int bufferLength, int flags) {
if ((flags & ~MSG_DONTWAIT) != 0) {
log.warn(String.format("sceNetInetSend unsupported flag 0x%X on socket", flags));
}
return send(buffer, bufferLength, flags, null);
}
public void blockedSend(BlockingSendState blockingState) {
if (blockingState.isTimeout()) {
if (log.isDebugEnabled()) {
log.debug(String.format("sceNetInetSend socket=0x%X returning %d (timeout)", getUid(), blockingState.sentLength));
}
setErrno(EAGAIN, blockingState);
unblockThread(blockingState, blockingState.sentLength);
} else {
int length = send(blockingState.buffer + blockingState.sentLength, blockingState.bufferLength - blockingState.sentLength, blockingState.flags, blockingState);
if (length > 0) {
unblockThread(blockingState, blockingState.sentLength);
}
}
}
public int sendto(int buffer, int bufferLength, int flags, pspNetSockAddrInternet toAddr) {
if ((flags & ~MSG_DONTWAIT) != 0) {
log.warn(String.format("sceNetInetSendto unsupported flag 0x%X on socket", flags));
}
return sendto(buffer, bufferLength, flags, toAddr, null);
}
public void blockedSendto(BlockingSendToState blockingState) {
if (blockingState.isTimeout()) {
if (log.isDebugEnabled()) {
log.debug(String.format("sceNetInetSendto socket=0x%X returning %d (timeout)", getUid(), blockingState.sentLength));
}
setErrno(EAGAIN, blockingState);
unblockThread(blockingState, blockingState.sentLength);
} else {
int length = sendto(blockingState.buffer + blockingState.sentLength, blockingState.bufferLength - blockingState.sentLength, blockingState.flags, blockingState.toAddr, blockingState);
if (length > 0) {
unblockThread(blockingState, blockingState.sentLength);
}
}
}
public int recvfrom(int buffer, int bufferLength, int flags, pspNetSockAddrInternet fromAddr) {
if ((flags & ~MSG_DONTWAIT) != 0) {
log.warn(String.format("sceNetInetRecvfrom unsupported flag 0x%X on socket", flags));
}
return recvfrom(buffer, bufferLength, flags, fromAddr, null);
}
public void blockedRecvfrom(BlockingReceiveFromState blockingState) {
if (blockingState.isTimeout()) {
if (log.isDebugEnabled()) {
log.debug(String.format("sceNetInetRecvfrom socket=0x%X returning %d (timeout)", getUid(), blockingState.receivedLength));
}
setErrno(EAGAIN, blockingState);
unblockThread(blockingState, blockingState.receivedLength);
} else {
int length = recvfrom(blockingState.buffer + blockingState.receivedLength, blockingState.bufferLength - blockingState.receivedLength, blockingState.flags, blockingState.fromAddr, blockingState);
if (length >= 0) {
unblockThread(blockingState, blockingState.receivedLength);
}
}
}
public int accept(pspNetSockAddrInternet sockAddrInternet) {
return accept(sockAddrInternet, null);
}
public void blockedAccept(BlockingAcceptState blockingState) {
int socketUid = accept(blockingState.acceptAddr, blockingState);
if (socketUid >= 0) {
unblockThread(blockingState, socketUid);
}
}
pspNetSockAddrInternet getLocalAddr() {
return localAddr;
}
public void setLocalAddr(pspNetSockAddrInternet localAddr) {
this.localAddr = localAddr;
}
pspNetSockAddrInternet getRemoteAddr() {
return remoteAddr;
}
public void setRemoteAddr(pspNetSockAddrInternet remoteAddr) {
this.remoteAddr = remoteAddr;
}
}
protected class pspInetStreamSocket extends pspInetSocket {
private SocketChannel socketChannel;
private ServerSocketChannel serverSocketChannel;
private boolean isServerSocket;
private SocketAddress pendingBindAddress;
private int backlog;
public pspInetStreamSocket(int uid) {
super(uid);
}
private void configureSocketChannel() throws IOException {
// We have to use non-blocking sockets at Java level
// to allow further PSP thread scheduling while the PSP is
// waiting for a blocking operation.
socketChannel.configureBlocking(false);
socketChannel.socket().setReceiveBufferSize(receiveBufferSize);
socketChannel.socket().setSendBufferSize(sendBufferSize);
socketChannel.socket().setKeepAlive(keepAlive);
socketChannel.socket().setReuseAddress(reuseAddress);
socketChannel.socket().setSoLinger(lingerEnabled, linger);
socketChannel.socket().setTcpNoDelay(tcpNoDelay);
// Connect has no timeout
socketChannel.socket().setSoTimeout(0);
}
private void openChannel() throws IOException {
if (isServerSocket) {
if (serverSocketChannel == null) {
serverSocketChannel = ServerSocketChannel.open();
// We have to use non-blocking sockets at Java level
// to allow further PSP thread scheduling while the PSP is
// waiting for a blocking operation.
serverSocketChannel.configureBlocking(false);
if (socketChannel != null) {
// If the socket was already bound, remember the bind address.
// It will be rebound in bindChannel().
if (socketChannel.socket().getLocalSocketAddress() != null) {
pendingBindAddress = socketChannel.socket().getLocalSocketAddress();
}
socketChannel.close();
socketChannel = null;
}
}
} else {
if (socketChannel == null) {
socketChannel = SocketChannel.open();
configureSocketChannel();
}
}
}
private void bindChannel() throws IOException {
if (pendingBindAddress != null) {
if (isServerSocket) {
serverSocketChannel.socket().bind(pendingBindAddress, backlog);
} else {
socketChannel.socket().bind(pendingBindAddress);
}
pendingBindAddress = null;
}
}
@Override
public boolean finishConnect() {
if (!isBlocking() && socketChannel.isConnectionPending()) {
// Try to finish the connection
try {
boolean connected = socketChannel.finishConnect();
if (connected) {
setLocalAddr(getNetSockAddrInternet(socketChannel.getLocalAddress()));
}
return connected;
} catch (IOException e) {
log.error(e);
setSocketError(e);
return false;
}
}
return true;
}
@Override
public int connect(pspNetSockAddrInternet addr) {
if (isServerSocket) {
log.error(String.format("connect not supported on server socket stream addr=%s, %s", addr.toString(), toString()));
return -1;
}
try {
openChannel();
bindChannel();
// On non-blocking, the connect might still be in progress
if (!finishConnect()) {
// Connect already in progress
setErrnoToSelf(EALREADY);
return -1;
}
if (socketChannel.isConnected()) {
// Already connected
setErrnoToSelf(EISCONN);
return -1;
}
boolean connected = socketChannel.connect(getSocketAddress(addr));
setRemoteAddr(addr);
if (isBlocking()) {
// blocking mode: wait for the connection to complete
while (!socketChannel.finishConnect()) {
try {
Thread.sleep(1);
} catch (InterruptedException e) {
// Ignore exception
}
}
} else if (!connected) {
// non-blocking mode: return EINPROGRESS
setErrnoToSelf(EINPROGRESS);
return -1;
}
} catch (IOException e) {
log.error(e);
setErrorToSelf(e);
return -1;
}
clearErrorToSelf();
return 0;
}
@Override
public int bind(pspNetSockAddrInternet addr) {
BlockingState blockingState = null;
try {
openChannel();
if (isServerSocket) {
pendingBindAddress = getSocketAddress(addr);
} else {
pendingBindAddress = null;
socketChannel.socket().bind(getSocketAddress(addr));
}
} catch (IOException e) {
log.error(e);
setError(e, blockingState);
return -1;
}
clearError(blockingState);
return 0;
}
@Override
public int recv(int buffer, int bufferLength, int flags, BlockingReceiveState blockingState) {
try {
// On non-blocking, the connect might still be in progress
if (!finishConnect()) {
setErrno(EAGAIN, blockingState);
return -1;
}
byte[] bytes = new byte[bufferLength];
int length = socketChannel.read(ByteBuffer.wrap(bytes));
storeBytes(buffer, length, bytes);
if (log.isDebugEnabled()) {
log.debug(String.format("sceNetInetRecv socket=0x%X received 0x%X bytes", getUid(), length));
if (log.isTraceEnabled() && length > 0) {
log.trace(String.format("Received data: %s", Utilities.getMemoryDump(buffer, length)));
}
}
// end of stream
if (length < 0) {
clearError(blockingState);
return 0;
}
// Nothing received on a non-blocking stream, return EAGAIN in errno
if (length == 0 && !isBlocking(flags)) {
if (bufferLength == 0) {
clearError(blockingState);
return 0;
}
setErrno(EAGAIN, blockingState);
return -1;
}
if (blockingState != null) {
blockingState.receivedLength += length;
}
// With a blocking stream, at least the low water mark has to be read
if (isBlocking(flags)) {
if (blockingState == null) {
blockingState = new BlockingReceiveState(this, buffer, bufferLength, flags, length);
}
// If we have not yet read as much as the low water mark,
// block the thread and retry later.
if (blockingState.receivedLength < getReceiveLowWaterMark() && length < bufferLength) {
blockThread(blockingState);
return -1;
}
}
clearError(blockingState);
return length;
} catch (IOException e) {
log.error(e);
setError(e, blockingState);
return -1;
}
}
@Override
public int send(int buffer, int bufferLength, int flags, BlockingSendState blockingState) {
try {
// On non-blocking, the connect might still be in progress
if (!finishConnect()) {
setError(ENOTCONN, blockingState);
return -1;
}
ByteBuffer byteBuffer = getByteBuffer(buffer, bufferLength);
int length = socketChannel.write(byteBuffer);
if (log.isDebugEnabled()) {
log.debug(String.format("sceNetInetSend socket=0x%X successfully sent 0x%X bytes", getUid(), length));
}
// Nothing sent on a non-blocking stream, return EAGAIN in errno
if (length == 0 && !isBlocking(flags)) {
setErrno(EAGAIN, blockingState);
return -1;
}
if (blockingState != null) {
blockingState.sentLength += length;
}
// With a blocking stream, we have to send all the bytes
if (isBlocking(flags)) {
if (blockingState == null) {
blockingState = new BlockingSendState(this, buffer, bufferLength, flags, length);
}
// If we have not yet sent all the bytes, block the thread
// and retry later
if (length < bufferLength) {
blockThread(blockingState);
return -1;
}
}
clearError(blockingState);
return length;
} catch (IOException e) {
log.error(e);
setError(e, blockingState);
return -1;
}
}
@Override
public int close() {
BlockingState blockingState = null;
if (socketChannel != null) {
try {
socketChannel.close();
socketChannel = null;
} catch (IOException e) {
log.error(e);
setError(e, blockingState);
return -1;
}
}
if (serverSocketChannel != null) {
try {
serverSocketChannel.close();
serverSocketChannel = null;
} catch (IOException e) {
log.error(e);
setError(e, blockingState);
return -1;
}
}
clearError(blockingState);
return 0;
}
@Override
public int recvfrom(int buffer, int bufferLength, int flags, pspNetSockAddrInternet fromAddr, BlockingReceiveFromState blockingState) {
log.warn("sceNetInetRecvfrom not supported on stream socket");
setError(-1, blockingState);
return -1;
}
@Override
public int sendto(int buffer, int bufferLength, int flags, pspNetSockAddrInternet toAddr, BlockingSendToState blockingState) {
log.warn("sceNetInetSendto not supported on stream socket");
setError(-1, blockingState);
return -1;
}
@Override
public SelectableChannel getSelectableChannel() {
if (isServerSocket) {
return serverSocketChannel;
}
return socketChannel;
}
@Override
public boolean isValid() {
if (isServerSocket) {
return serverSocketChannel != null;
}
if (socketChannel == null) {
return false;
}
if (socketChannel.isConnectionPending()) {
// Finish the connection otherwise, the channel will never
// be readable/writable
try {
socketChannel.finishConnect();
} catch (IOException e) {
if (log.isDebugEnabled()) {
log.debug(String.format("%s: %s", toString(), e.toString()));
}
return false;
}
} else if (!socketChannel.isConnected()) {
return false;
}
return !socketChannel.socket().isClosed();
}
@Override
public int setReceiveBufferSize(int receiveBufferSize) {
super.setReceiveBufferSize(receiveBufferSize);
if (socketChannel != null) {
try {
socketChannel.socket().setReceiveBufferSize(receiveBufferSize);
} catch (SocketException e) {
setErrorToSelf(e);
return -1;
}
}
return 0;
}
@Override
public int setSendBufferSize(int sendBufferSize) {
super.setSendBufferSize(sendBufferSize);
if (socketChannel != null) {
try {
socketChannel.socket().setSendBufferSize(sendBufferSize);
} catch (SocketException e) {
setErrorToSelf(e);
return -1;
}
}
return 0;
}
@Override
public int getPeername(pspNetSockAddrInternet sockAddrInternet) {
if (socketChannel == null) {
return -1;
}
InetAddress inetAddress = socketChannel.socket().getInetAddress();
sockAddrInternet.readFromInetAddress(inetAddress, getRemoteAddr());
return 0;
}
@Override
public int getSockname(pspNetSockAddrInternet sockAddrInternet) {
if (socketChannel == null) {
return -1;
}
InetAddress inetAddress = socketChannel.socket().getLocalAddress();
sockAddrInternet.readFromInetAddress(inetAddress, getLocalAddr());
return 0;
}
@Override
public int setKeepAlive(boolean keepAlive) {
super.setKeepAlive(keepAlive);
if (socketChannel != null) {
try {
socketChannel.socket().setKeepAlive(keepAlive);
} catch (SocketException e) {
log.error(e);
setErrorToSelf(e);
return -1;
}
}
return 0;
}
@Override
public int setLinger(boolean enabled, int linger) {
super.setLinger(enabled, linger);
if (socketChannel != null) {
try {
socketChannel.socket().setSoLinger(enabled, linger);
} catch (SocketException e) {
log.error(e);
setErrorToSelf(e);
return -1;
}
}
return 0;
}
@Override
public int setReuseAddress(boolean reuseAddress) {
super.setReuseAddress(reuseAddress);
if (socketChannel != null) {
try {
socketChannel.socket().setReuseAddress(reuseAddress);
} catch (SocketException e) {
log.error(e);
setErrorToSelf(e);
return -1;
}
}
return 0;
}
@Override
public int setTcpNoDelay(boolean tcpNoDelay) {
super.setTcpNoDelay(tcpNoDelay);
if (socketChannel != null) {
try {
socketChannel.socket().setTcpNoDelay(tcpNoDelay);
} catch (SocketException e) {
log.error(e);
setErrorToSelf(e);
return -1;
}
}
return 0;
}
@Override
public int shutdown(int how) {
if (socketChannel != null) {
try {
switch (how) {
case SHUT_RD:
socketChannel.socket().shutdownInput();
break;
case SHUT_WR:
socketChannel.socket().shutdownOutput();
break;
case SHUT_RDWR:
socketChannel.socket().shutdownInput();
socketChannel.socket().shutdownOutput();
break;
}
} catch (IOException e) {
log.error(e);
setErrorToSelf(e);
return -1;
}
}
return 0;
}
@Override
public int listen(int backlog) {
isServerSocket = true;
this.backlog = backlog;
try {
openChannel();
bindChannel();
} catch (IOException e) {
log.error(e);
setErrorToSelf(e);
return -1;
}
return 0;
}
@Override
public int accept(pspNetSockAddrInternet sockAddrInternet) {
if (!isServerSocket) {
log.error(String.format("sceNetInetAccept on non-server socket stream not allowed addr=%s, %s", sockAddrInternet.toString(), toString()));
return -1;
}
return super.accept(sockAddrInternet);
}
@Override
public int accept(pspNetSockAddrInternet sockAddrInternet, BlockingAcceptState blockingState) {
SocketChannel socketChannel;
try {
openChannel();
bindChannel();
socketChannel = serverSocketChannel.accept();
} catch (IOException e) {
log.error(e);
setError(e, blockingState);
return -1;
}
if (socketChannel == null) {
if (isBlocking()) {
if (blockingState == null) {
blockingState = new BlockingAcceptState(this, sockAddrInternet);
}
blockThread(blockingState);
return -1;
}
setErrno(EWOULDBLOCK, blockingState);
return -1;
}
pspInetStreamSocket inetSocket = (pspInetStreamSocket) createSocket(SOCK_STREAM, 0);
inetSocket.socketChannel = socketChannel;
inetSocket.copySocketAttributes(this);
try {
inetSocket.configureSocketChannel();
} catch (IOException e) {
log.error(e);
}
sockAddrInternet.readFromInetSocketAddress((InetSocketAddress) socketChannel.socket().getRemoteSocketAddress());
sockAddrInternet.write(Memory.getInstance());
if (log.isDebugEnabled()) {
log.debug(String.format("sceNetInetAccept accepted connection from %s on socket %s", sockAddrInternet.toString(), inetSocket.toString()));
} else if (log.isInfoEnabled()) {
log.info(String.format("sceNetInetAccept accepted connection from %s", sockAddrInternet.toString()));
}
return inetSocket.getUid();
}
}
protected class pspInetDatagramSocket extends pspInetSocket {
private DatagramChannel datagramChannel;
public pspInetDatagramSocket(int uid) {
super(uid);
// Datagrams have different default buffer sizes
receiveBufferSize = 41600;
sendBufferSize = 9216;
}
private void openChannel() throws IOException {
if (datagramChannel == null) {
datagramChannel = DatagramChannel.open();
// We have to use non-blocking sockets at Java level
// to allow further PSP thread scheduling while the PSP is
// waiting for a blocking operation.
datagramChannel.configureBlocking(false);
datagramChannel.socket().setReceiveBufferSize(receiveBufferSize);
datagramChannel.socket().setSendBufferSize(sendBufferSize);
}
}
@Override
public int connect(pspNetSockAddrInternet addr) {
try {
openChannel();
datagramChannel.connect(getSocketAddress(addr));
setRemoteAddr(addr);
} catch (IOException e) {
log.error(e);
setErrorToSelf(e);
return -1;
}
clearErrorToSelf();
return 0;
}
@Override
public int bind(pspNetSockAddrInternet addr) {
try {
openChannel();
datagramChannel.socket().bind(getSocketAddress(addr));
} catch (IOException e) {
log.error(e);
setErrorToSelf(e);
return -1;
}
clearErrorToSelf();
return 0;
}
@Override
public int close() {
if (datagramChannel != null) {
try {
datagramChannel.close();
datagramChannel = null;
} catch (IOException e) {
log.error(e);
setErrorToSelf(e);
return -1;
}
}
clearErrorToSelf();
return 0;
}
@Override
public int recv(int buffer, int bufferLength, int flags, BlockingReceiveState blockingState) {
try {
byte[] bytes = new byte[bufferLength];
ByteBuffer byteBuffer = ByteBuffer.wrap(bytes);
SocketAddress socketAddress = datagramChannel.receive(byteBuffer);
int length = byteBuffer.position();
storeBytes(buffer, length, bytes);
if (log.isDebugEnabled()) {
log.debug(String.format("sceNetInetRecv socket=0x%X received 0x%X bytes from %s", getUid(), length, socketAddress));
if (log.isTraceEnabled() && length > 0) {
log.trace(String.format("Received data: %s", Utilities.getMemoryDump(buffer, length)));
}
}
if (length < 0) {
// end of stream
clearError(blockingState);
return 0;
}
// Nothing received on a non-blocking stream, return EAGAIN in errno
if (length == 0 && !isBlocking(flags)) {
if (bufferLength == 0) {
clearError(blockingState);
return 0;
}
setErrno(EAGAIN, blockingState);
return -1;
}
if (blockingState != null) {
blockingState.receivedLength += length;
}
// With a blocking stream, at least the low water mark has to be read
if (isBlocking(flags)) {
if (blockingState == null) {
blockingState = new BlockingReceiveState(this, buffer, bufferLength, flags, length);
}
// If we have not yet read as much as the low water mark,
// block the thread and retry later.
if (blockingState.receivedLength < getReceiveLowWaterMark() && length < bufferLength) {
blockThread(blockingState);
return -1;
}
}
clearError(blockingState);
return length;
} catch (IOException e) {
log.error(e);
setError(e, blockingState);
return -1;
}
}
@Override
public int send(int buffer, int bufferLength, int flags, BlockingSendState blockingState) {
log.warn("sceNetInetSend not supported on datagram socket");
setError(-1, blockingState);
return -1;
}
@Override
public int recvfrom(int buffer, int bufferLength, int flags, pspNetSockAddrInternet fromAddr, BlockingReceiveFromState blockingState) {
try {
byte[] bytes = new byte[bufferLength];
ByteBuffer byteBuffer = ByteBuffer.wrap(bytes);
SocketAddress socketAddress = datagramChannel.receive(byteBuffer);
int length = byteBuffer.position();
storeBytes(buffer, length, bytes);
if (log.isDebugEnabled()) {
log.debug(String.format("sceNetInetRecvfrom socket=0x%X received 0x%X bytes from %s", getUid(), length, socketAddress));
if (log.isTraceEnabled() && length > 0) {
log.trace(String.format("Received data: %s", Utilities.getMemoryDump(buffer, length)));
}
}
if (socketAddress == null) {
// Nothing received on a non-blocking datagram, return EAGAIN in errno
if (!isBlocking(flags)) {
setErrno(EAGAIN, blockingState);
return -1;
}
// Nothing received on a blocking datagram, block the thread
if (blockingState == null) {
blockingState = new BlockingReceiveFromState(this, buffer, bufferLength, flags, fromAddr, length);
}
blockThread(blockingState);
return -1;
}
if (socketAddress instanceof InetSocketAddress) {
InetSocketAddress inetSocketAddress = (InetSocketAddress) socketAddress;
fromAddr.readFromInetSocketAddress(inetSocketAddress);
fromAddr.write(Memory.getInstance());
}
clearError(blockingState);
return length;
} catch (IOException e) {
log.error(e);
setError(e, blockingState);
return -1;
}
}
@Override
public int sendto(int buffer, int bufferLength, int flags, pspNetSockAddrInternet toAddr, BlockingSendToState blockingState) {
try {
openChannel();
ByteBuffer byteBuffer = getByteBuffer(buffer, bufferLength);
SocketAddress socketAddress = getSocketAddress(toAddr);
int length = datagramChannel.send(byteBuffer, socketAddress);
if (log.isDebugEnabled()) {
log.debug(String.format("sceNetInetSendto socket=0x%X successfully sent 0x%X bytes", getUid(), length));
}
// Nothing sent on a non-blocking stream, return EAGAIN in errno
if (length == 0 && !isBlocking(flags)) {
setErrno(EAGAIN, blockingState);
return -1;
}
if (blockingState != null) {
blockingState.sentLength += length;
}
// With a blocking stream, we have to send all the bytes
if (isBlocking(flags)) {
if (blockingState == null) {
blockingState = new BlockingSendToState(this, buffer, bufferLength, flags, toAddr, length);
}
// If we have not yet sent all the bytes, block the thread
// and retry later
if (length < bufferLength) {
blockThread(blockingState);
return -1;
}
}
clearError(blockingState);
return length;
} catch (IOException e) {
log.error(e);
setError(e, blockingState);
return -1;
}
}
@Override
public int setBroadcast(boolean broadcast) {
super.setBroadcast(broadcast);
try {
openChannel();
datagramChannel.socket().setBroadcast(broadcast);
} catch (IOException e) {
log.error(e);
setErrorToSelf(e);
return -1;
}
clearErrorToSelf();
return 0;
}
@Override
public SelectableChannel getSelectableChannel() {
return datagramChannel;
}
@Override
public boolean isValid() {
return datagramChannel != null && !datagramChannel.socket().isClosed();
}
@Override
public int setReceiveBufferSize(int receiveBufferSize) {
super.setReceiveBufferSize(receiveBufferSize);
if (datagramChannel != null) {
try {
datagramChannel.socket().setReceiveBufferSize(receiveBufferSize);
} catch (SocketException e) {
setErrorToSelf(e);
return -1;
}
}
return 0;
}
@Override
public int setSendBufferSize(int sendBufferSize) {
super.setSendBufferSize(sendBufferSize);
if (datagramChannel != null) {
try {
datagramChannel.socket().setSendBufferSize(sendBufferSize);
} catch (SocketException e) {
setErrorToSelf(e);
return -1;
}
}
return 0;
}
@Override
public int getPeername(pspNetSockAddrInternet sockAddrInternet) {
if (datagramChannel == null) {
return -1;
}
InetAddress inetAddress = datagramChannel.socket().getInetAddress();
sockAddrInternet.readFromInetAddress(inetAddress, getRemoteAddr());
return 0;
}
@Override
public int getSockname(pspNetSockAddrInternet sockAddrInternet) {
if (datagramChannel == null) {
return -1;
}
InetAddress inetAddress = datagramChannel.socket().getLocalAddress();
sockAddrInternet.readFromInetAddress(inetAddress, getLocalAddr());
return 0;
}
@Override
public int setReuseAddress(boolean reuseAddress) {
super.setReuseAddress(reuseAddress);
if (datagramChannel != null) {
try {
datagramChannel.socket().setReuseAddress(reuseAddress);
} catch (SocketException e) {
log.error(e);
setErrorToSelf(e);
return -1;
}
}
return 0;
}
@Override
public int shutdown(int how) {
log.error(String.format("Shutdown not supported on datagram socket: how=%d, %s", how, toString()));
return -1;
}
@Override
public boolean finishConnect() {
// Nothing to do for datagrams
return true;
}
@Override
public int listen(int backlog) {
log.error(String.format("Listen not supported on datagram socket: backlog=%d, %s", backlog, toString()));
return -1;
}
@Override
public int accept(pspNetSockAddrInternet sockAddrInternet, BlockingAcceptState blockingState) {
log.error(String.format("Accept not supported on datagram socket: sockAddrInternet=%s, %s", sockAddrInternet.toString(), toString()));
return -1;
}
}
protected class pspInetRawSocket extends pspInetSocket {
private RawChannel rawChannel;
private int protocol;
private boolean isAvailable;
public pspInetRawSocket(int uid, int protocol) {
super(uid);
this.protocol = protocol;
isAvailable = true;
}
protected boolean openChannel() throws IllegalStateException {
if (!isAvailable) {
return false;
}
if (rawChannel == null) {
try {
rawChannel = new RawChannel();
} catch (UnsatisfiedLinkError e) {
log.error(String.format("The rocksaw library is not available on your system (%s). This library is required to implement RAW sockets. Disabling this feature.", e.toString()));
isAvailable = false;
return false;
}
try {
rawChannel.socket().open(RawSocket.PF_INET, protocol);
// Use non-blocking IO's
rawChannel.configureBlocking(false);
} catch (IOException e) {
log.error(String.format("You need to start Jpcsp with administator right to be able to open RAW sockets (%s). Disabling this feature.", e.toString()));
isAvailable = false;
return false;
}
}
return rawChannel.socket().isOpen();
}
@Override
public int bind(pspNetSockAddrInternet addr) {
if (!openChannel()) {
return -1;
}
try {
rawChannel.socket().bind(getInetAddress(addr));
} catch (IllegalStateException e) {
log.error(e);
return -1;
} catch (UnknownHostException e) {
log.error(e);
setErrorToSelf(e);
return -1;
} catch (IOException e) {
log.error(e);
setErrorToSelf(e);
return -1;
}
return 0;
}
@Override
public int close() {
if (rawChannel != null) {
try {
rawChannel.close();
} catch (IOException e) {
log.error(e);
setErrorToSelf(e);
return -1;
} finally {
rawChannel = null;
}
}
return 0;
}
@Override
public int connect(pspNetSockAddrInternet addr) {
log.error(String.format("sceNetInetConnect is not supported on Raw sockets: %s", toString()));
return -1;
}
@Override
public boolean finishConnect() {
return openChannel();
}
@Override
public int getPeername(pspNetSockAddrInternet sockAddrInternet) {
log.error(String.format("sceNetInetGetpeername is not supported on Raw sockets: %s", toString()));
return -1;
}
@Override
public SelectableChannel getSelectableChannel() {
if (!openChannel()) {
return null;
}
return rawChannel;
}
@Override
public int getSockname(pspNetSockAddrInternet sockAddrInternet) {
log.error(String.format("sceNetInetGetsockname is not supported on Raw sockets: %s", toString()));
return -1;
}
@Override
public boolean isValid() {
return openChannel();
}
@Override
public int listen(int backlog) {
log.error(String.format("sceNetInetListen is not supported on Raw sockets: %s", toString()));
return -1;
}
@Override
public int recv(int buffer, int bufferLength, int flags, BlockingReceiveState blockingState) {
log.error(String.format("sceNetInetRecv is not supported on Raw sockets: %s", toString()));
return -1;
}
@Override
public int recvfrom(int buffer, int bufferLength, int flags, pspNetSockAddrInternet fromAddr, BlockingReceiveFromState blockingState) {
try {
if (!openChannel()) {
return -1;
}
// Nothing available for read?
if (!rawChannel.socket().isSelectedForRead()) {
if (!isBlocking(flags)) {
// Nothing received on a non-blocking stream, return EAGAIN in errno
setErrno(EAGAIN, blockingState);
return -1;
}
if (blockingState == null) {
blockingState = new BlockingReceiveFromState(this, buffer, bufferLength, flags, fromAddr, 0);
}
// Block the thread and retry later.
blockThread(blockingState);
return -1;
}
byte[] bytes = new byte[bufferLength];
byte[] address = new byte[4];
int length = rawChannel.socket().read(bytes, address);
storeBytes(buffer, length, bytes);
if (blockingState != null) {
blockingState.receivedLength += length;
}
fromAddr.sin_family = AF_INET;
fromAddr.sin_addr = bytesToInternetAddress(address);
fromAddr.write(Memory.getInstance());
if (log.isDebugEnabled()) {
log.debug(String.format("sceNetInetRecvfrom socket=0x%X received 0x%X bytes from %s", getUid(), length, fromAddr));
if (log.isTraceEnabled() && length > 0) {
log.trace(String.format("Received data: %s", Utilities.getMemoryDump(buffer, length)));
}
}
return length;
} catch (InterruptedIOException e) {
log.error(e);
setError(e, blockingState);
return -1;
} catch (IOException e) {
log.error(e);
setError(e, blockingState);
return -1;
}
}
@Override
public int send(int buffer, int bufferLength, int flags, BlockingSendState blockignState) {
log.error(String.format("sceNetInetSend is not supported on Raw sockets: %s", toString()));
return -1;
}
@Override
public int sendto(int buffer, int bufferLength, int flags, pspNetSockAddrInternet toAddr, BlockingSendToState blockingState) {
try {
if (!openChannel()) {
return -1;
}
// Ready for write?
if (!rawChannel.socket().isSelectedForWrite()) {
if (!isBlocking(flags)) {
// Nothing sent on a non-blocking stream, return EAGAIN in errno
setErrno(EAGAIN, blockingState);
return -1;
}
// With a blocking stream, we have to send all the bytes
if (blockingState == null) {
blockingState = new BlockingSendToState(this, buffer, bufferLength, flags, toAddr, 0);
}
// Block the thread and retry later
blockThread(blockingState);
return -1;
}
InetAddress inetAddress = getInetAddress(toAddr);
byte[] data = getByteArray(buffer, bufferLength);
int length = rawChannel.socket().write(inetAddress, data);
if (log.isDebugEnabled()) {
log.debug(String.format("sceNetInetSendto socket=0x%X successfully sent 0x%X bytes", getUid(), length));
}
if (blockingState != null) {
blockingState.sentLength += length;
}
return length;
} catch (IllegalStateException e) {
log.error(e);
return -1;
} catch (IOException e) {
log.error(e);
setError(e, blockingState);
return -1;
}
}
@Override
public int shutdown(int how) {
log.warn(String.format("sceNetInetShutdown is not supported on Raw sockets: %s", toString()));
return 0;
}
@Override
public Selector getSelector(Selector selector, RawSelector rawSelector) {
return rawSelector;
}
@Override
public int accept(pspNetSockAddrInternet sockAddrInternet, BlockingAcceptState blockingState) {
log.error(String.format("sceNetInetAccept is not supported on Raw sockets: %s", toString()));
return -1;
}
}
protected static class pspInetPollFd extends pspAbstractMemoryMappedStructure {
public int fd;
public int events;
public int revents;
@Override
protected void read() {
fd = read32();
events = read16();
revents = read16();
}
@Override
protected void write() {
write32(fd);
write16((short) events);
write16((short) revents);
}
@Override
public int sizeof() {
return 8;
}
@Override
public String toString() {
return String.format("PollFd[fd=%d, events=0x%04X(%s), revents=0x%04X(%s)]", fd, events, getPollEventName(events), revents, getPollEventName(revents));
}
}
protected HashMap<Integer, pspInetSocket> sockets;
protected static final String idPurpose = "sceNetInet-socket";
private InetAddress[] doProxyInetAddresses;
@Override
public void start() {
setSettingsListener("network.broadcastAddress", new BroadcastAddressSettingsListener());
sockets = new HashMap<Integer, pspInetSocket>();
doProxyInetAddresses = null;
// Perform long running actions in a separate thread to not brake
// the start of the emulator
AsyncStartThread asyncStartThread = new AsyncStartThread();
asyncStartThread.setName("sceNetInet Async Start Thread");
asyncStartThread.start();
super.start();
}
@Override
public void stop() {
// Close all the open sockets
for (pspInetSocket inetSocket : sockets.values()) {
inetSocket.close();
}
sockets.clear();
super.stop();
}
/**
* Set the errno to an error value.
* Each thread has its own errno.
*
* @param errno
*/
public static void setErrnoToSelf(int errno) {
setErrno(errno, Modules.ThreadManForUserModule.getCurrentThread());
}
private static void setErrno(int errno, SceKernelThreadInfo thread) {
thread.errno = errno;
}
private static void setErrno(int errno, BlockingState blockingState) {
if (blockingState == null) {
setErrnoToSelf(errno);
} else {
setErrno(errno, Modules.ThreadManForUserModule.getThreadById(blockingState.threadId));
}
}
/**
* Return the current value of the errno.
* Each thread has its own errno.
*
* @return the errno of the current thread
*/
public static int getErrno() {
return Modules.ThreadManForUserModule.getCurrentThread().errno;
}
protected int createSocketId() {
// A socket ID has to be a number [1..255],
// because sceNetInetSelect can handle only 256 bits.
// 0 is considered by LuaPLayer as an invalid value as well.
return SceUidManager.getNewId(idPurpose, 1, 255);
}
protected pspInetSocket createSocket(int type, int protocol) {
int uid = createSocketId();
pspInetSocket inetSocket = null;
if (type == SOCK_STREAM) {
inetSocket = new pspInetStreamSocket(uid);
} else if (type == SOCK_DGRAM) {
inetSocket = new pspInetDatagramSocket(uid);
} else if (type == SOCK_RAW) {
inetSocket = new pspInetRawSocket(uid, protocol);
} else if (type == SOCK_DGRAM_UNKNOWN_6) {
inetSocket = new pspInetDatagramSocket(uid);
} else if (type == SOCK_STREAM_UNKNOWN_10) {
inetSocket = new pspInetStreamSocket(uid);
}
sockets.put(uid, inetSocket);
return inetSocket;
}
protected void releaseSocketId(int id) {
SceUidManager.releaseId(id, idPurpose);
}
protected int readSocketList(Selector selector, RawSelector rawSelector, TPointer address, int n, int selectorOperation, String comment) {
int closedSocketsCount = 0;
if (address.isNotNull()) {
LinkedList<Integer> closedChannels = new LinkedList<Integer>();
int length = (n + 7) / 8;
if (selectorOperation != 0) {
IMemoryReader memoryReader = MemoryReader.getMemoryReader(address.getAddress(), length, 4);
int value = 0;
for (int socket = 0; socket < n; socket++) {
if ((socket % 32) == 0) {
value = memoryReader.readNext();
}
int bit = (value & 1);
value = value >>> 1;
if (bit != 0) {
pspInetSocket inetSocket = sockets.get(socket);
if (inetSocket != null) {
SelectableChannel selectableChannel = inetSocket.getSelectableChannel();
if (selectableChannel != null) {
int registeredOperation = selectorOperation & selectableChannel.validOps();
if (registeredOperation != 0) {
Selector socketSelector = inetSocket.getSelector(selector, rawSelector);
// A channel may be registered at most once with any particular selector
if (selectableChannel.isRegistered()) {
// If the channel is already registered,
// add the new operation to the active registration
SelectionKey selectionKey = selectableChannel.keyFor(socketSelector);
selectionKey.interestOps(selectionKey.interestOps() | registeredOperation);
} else {
try {
selectableChannel.register(socketSelector, registeredOperation, new Integer(socket));
} catch (ClosedChannelException e) {
closedChannels.add(socket);
if (log.isDebugEnabled()) {
log.debug(String.format("%s: %s", inetSocket.toString(), e.toString()));
}
}
}
}
}
}
}
}
}
// Clear the socket list so that we just have to set the bits for
// the sockets that are ready.
address.clear(length);
// and set the bit for all the closed channels
for (Integer socket : closedChannels) {
setSelectBit(address, socket);
closedSocketsCount++;
}
}
return closedSocketsCount;
}
protected String dumpSelectBits(TPointer addr, int n) {
if (addr.isNull() || n <= 0) {
return "";
}
StringBuilder dump = new StringBuilder();
for (int socket = 0; socket < n; socket++) {
int bit = 1 << (socket % 8);
int value = addr.getValue8(socket / 8);
if ((value & bit) != 0) {
if (dump.length() > 0) {
dump.append(", ");
}
dump.append(String.format("%d", socket));
}
}
return dump.toString();
}
protected void setSelectBit(TPointer addr, int socket) {
if (addr.isNotNull()) {
int offset = socket / 8;
int value = 1 << (socket % 8);
addr.setValue8(offset, (byte) (addr.getValue8(offset) | value));
}
}
protected void blockThread(BlockingState blockingState) {
if (!blockingState.threadBlocked) {
if (log.isDebugEnabled()) {
log.debug(String.format("Blocking the current thread %s", Modules.ThreadManForUserModule.getCurrentThread().toString()));
}
Modules.ThreadManForUserModule.hleBlockCurrentThread(SceKernelThreadInfo.JPCSP_WAIT_NET, blockingState);
blockingState.threadBlocked = true;
}
long schedule = Emulator.getClock().microTime() + BLOCKED_OPERATION_POLLING_MICROS;
Emulator.getScheduler().addAction(schedule, blockingState);
}
protected void unblockThread(BlockingState blockingState, int returnValue) {
SceKernelThreadInfo thread = Modules.ThreadManForUserModule.getThreadById(blockingState.threadId);
if (thread != null) {
thread.cpuContext._v0 = returnValue;
}
if (blockingState.threadBlocked) {
if (log.isDebugEnabled()) {
log.debug(String.format("Unblocking the thread %s", thread.toString()));
}
Modules.ThreadManForUserModule.hleUnblockThread(blockingState.threadId);
blockingState.threadBlocked = false;
}
}
protected int checkInvalidSelectedSockets(BlockingSelectState blockingState) {
int countInvalidSocket = 0;
// Check for valid sockets.
// When a socket is no longer valid (e.g. connect failed),
// return the select bit for this socket so that the application
// has a chance to see the failed connection.
for (SelectionKey selectionKey : blockingState.selector.keys()) {
if (selectionKey.isValid()) {
int socket = (Integer) selectionKey.attachment();
pspInetSocket inetSocket = sockets.get(socket);
if (inetSocket == null || !inetSocket.isValid()) {
countInvalidSocket++;
int interestOps;
try {
interestOps = selectionKey.interestOps();
} catch (CancelledKeyException e) {
// The key has been cancelled, set the selection bit for all operations
interestOps = SelectionKey.OP_READ | SelectionKey.OP_WRITE | SelectionKey.OP_CONNECT | SelectionKey.OP_ACCEPT;
}
if ((interestOps & readSelectionKeyOperations) != 0) {
setSelectBit(blockingState.readSocketsAddr, socket);
}
if ((interestOps & writeSelectionKeyOperations) != 0) {
setSelectBit(blockingState.writeSocketsAddr, socket);
}
// Out-of-band data is not implemented (not supported by Java?)
}
}
}
if (log.isTraceEnabled()) {
log.trace(String.format("checkInvalidSelectedSockets returns %d", countInvalidSocket));
}
return countInvalidSocket;
}
protected void setPollResult(pspInetPollFd[] pollFds, int socket, int revents) {
for (int i = 0; i < pollFds.length; i++) {
if (pollFds[i].fd == socket) {
pollFds[i].revents |= revents;
break;
}
}
}
protected void blockedPoll(BlockingPollState blockingState) {
try {
// Try to finish all the pending connections
for (int i = 0; i < blockingState.pollFds.length; i++) {
pspInetSocket inetSocket = sockets.get(blockingState.pollFds[i].fd);
if (inetSocket != null) {
inetSocket.finishConnect();
}
}
// We do not want to block here on the selector, call selectNow
int count = blockingState.selector.selectNow();
boolean threadBlocked;
if (count <= 0) {
// Check for timeout
if (blockingState.isTimeout()) {
// Timeout, unblock the thread and return 0
if (log.isDebugEnabled()) {
log.debug(String.format("sceNetInetPoll returns %d sockets (timeout)", count));
}
threadBlocked = false;
} else {
// No timeout, keep blocking the thread
threadBlocked = true;
}
} else {
// Some sockets are ready, unblock the thread and return the count
if (log.isDebugEnabled()) {
log.debug(String.format("sceNetInetPoll returns %d sockets", count));
}
for (Iterator<SelectionKey> it = blockingState.selector.selectedKeys().iterator(); it.hasNext(); ) {
// Retrieve the next key and remove it from the set
SelectionKey selectionKey = it.next();
it.remove();
if (selectionKey.isReadable()) {
int socket = (Integer) selectionKey.attachment();
setPollResult(blockingState.pollFds, socket, POLLIN | POLLRDNORM);
}
if (selectionKey.isWritable()) {
int socket = (Integer) selectionKey.attachment();
setPollResult(blockingState.pollFds, socket, POLLOUT);
}
}
threadBlocked = false;
}
if (threadBlocked) {
blockThread(blockingState);
} else {
// We do no longer need the selector, close it
blockingState.selector.close();
// Write back the updated revents fields
Memory mem = Memory.getInstance();
for (int i = 0; i < blockingState.pollFds.length; i++) {
blockingState.pollFds[i].write(mem);
if (log.isDebugEnabled()) {
log.debug(String.format("sceNetInetPoll returning pollFd[%d]=%s", i, blockingState.pollFds[i].toString()));
}
}
// sceNetInetPoll can now return the count, unblock the thread
unblockThread(blockingState, count);
}
} catch (IOException e) {
log.error(e);
}
}
protected void processSelectedKey(Selector selector, BlockingSelectState blockingState) {
for (Iterator<SelectionKey> it = selector.selectedKeys().iterator(); it.hasNext(); ) {
// Retrieve the next key and remove it from the set
SelectionKey selectionKey = it.next();
it.remove();
if ((selectionKey.readyOps() & readSelectionKeyOperations) != 0) {
int socket = (Integer) selectionKey.attachment();
setSelectBit(blockingState.readSocketsAddr, socket);
}
if ((selectionKey.readyOps() & writeSelectionKeyOperations) != 0) {
int socket = (Integer) selectionKey.attachment();
setSelectBit(blockingState.writeSocketsAddr, socket);
}
}
}
protected void blockedSelect(BlockingSelectState blockingState) {
try {
// Try to finish all the pending connections
for (Iterator<SelectionKey> it = blockingState.selector.keys().iterator(); it.hasNext(); ) {
SelectionKey selectionKey = it.next();
Integer socket = (Integer) selectionKey.attachment();
pspInetSocket inetSocket = sockets.get(socket);
if (inetSocket != null) {
inetSocket.finishConnect();
}
}
// Start with the count of closed channels
// (detected when registering the selector)
int count = blockingState.count;
// We do not want to block here on the selector, call selectNow
count += blockingState.selector.selectNow();
count += blockingState.rawSelector.selectNow();
// add any socket becoming invalid (e.g. connect failed)
count += checkInvalidSelectedSockets(blockingState);
boolean threadBlocked;
if (count <= 0) {
// Check for timeout
if (blockingState.isTimeout()) {
// Timeout, unblock the thread and return 0
if (log.isDebugEnabled()) {
log.debug(String.format("sceNetInetSelect returns %d sockets (timeout)", count));
}
threadBlocked = false;
} else {
// No timeout, keep blocking the thread
threadBlocked = true;
}
} else {
// Some sockets are ready, unblock the thread and return the count
if (log.isDebugEnabled()) {
log.debug(String.format("sceNetInetSelect returns %d sockets", count));
}
processSelectedKey(blockingState.selector, blockingState);
processSelectedKey(blockingState.rawSelector, blockingState);
threadBlocked = false;
}
if (threadBlocked) {
blockThread(blockingState);
} else {
// We do no longer need the selectors, close them
blockingState.selector.close();
blockingState.rawSelector.close();
if (log.isDebugEnabled() && count > 0) {
if (blockingState.readSocketsAddr.isNotNull()) {
log.debug(String.format("sceNetInetSelect returning Read Sockets : %s", dumpSelectBits(blockingState.readSocketsAddr, blockingState.numberSockets)));
}
if (blockingState.writeSocketsAddr.isNotNull()) {
log.debug(String.format("sceNetInetSelect returning Write Sockets : %s", dumpSelectBits(blockingState.writeSocketsAddr, blockingState.numberSockets)));
}
if (blockingState.outOfBandSocketsAddr.isNotNull()) {
log.debug(String.format("sceNetInetSelect returning Out-of-band Sockets: %s", dumpSelectBits(blockingState.outOfBandSocketsAddr, blockingState.numberSockets)));
}
}
// sceNetInetSelect can now return the count, unblock the thread
unblockThread(blockingState, count);
}
} catch (IOException e) {
log.error(e);
}
}
public static String internetAddressToString(int address) {
int n4 = (address >> 24) & 0xFF;
int n3 = (address >> 16) & 0xFF;
int n2 = (address >> 8) & 0xFF;
int n1 = (address ) & 0xFF;
return String.format("%d.%d.%d.%d", n1, n2, n3, n4);
}
public static int bytesToInternetAddress(byte[] bytes) {
if (bytes == null) {
return 0;
}
int inetAddress = 0;
for (int i = bytes.length - 1; i >= 0; i--) {
inetAddress = (inetAddress << 8) | (bytes[i] & 0xFF);
}
return inetAddress;
}
public static byte[] internetAddressToBytes(int address) {
byte[] bytes = new byte[4];
int n4 = (address >> 24) & 0xFF;
int n3 = (address >> 16) & 0xFF;
int n2 = (address >> 8) & 0xFF;
int n1 = (address ) & 0xFF;
bytes[3] = (byte) n4;
bytes[2] = (byte) n3;
bytes[1] = (byte) n2;
bytes[0] = (byte) n1;
return bytes;
}
protected static String getSocketTypeNameString(int type) {
if (type < 0 || type >= socketTypeNames.length) {
return String.format("Unknown type %d", type);
}
return socketTypeNames[type];
}
protected static String getPollEventName(int event) {
StringBuilder name = new StringBuilder();
if ((event & POLLIN) != 0) {
name.append("|POLLIN");
}
if ((event & POLLPRI) != 0) {
name.append("|POLLPRI");
}
if ((event & POLLOUT) != 0) {
name.append("|POLLOUT");
}
if ((event & POLLERR) != 0) {
name.append("|POLLERR");
}
if ((event & POLLHUP) != 0) {
name.append("|POLLHUP");
}
if ((event & POLLNVAL) != 0) {
name.append("|POLLNVAL");
}
if ((event & POLLRDNORM) != 0) {
name.append("|POLLRDNORM");
}
if ((event & POLLRDBAND) != 0) {
name.append("|POLLRDBAND");
}
if ((event & POLLWRBAND) != 0) {
name.append("|POLLWRBAND");
}
if (name.length() > 0 && name.charAt(0) == '|') {
name.deleteCharAt(0);
}
return name.toString();
}
protected static String getOptionNameString(int optionName) {
switch (optionName) {
case SO_DEBUG: return "SO_DEBUG";
case SO_ACCEPTCONN: return "SO_ACCEPTCONN";
case SO_REUSEADDR: return "SO_REUSEADDR";
case SO_KEEPALIVE: return "SO_KEEPALIVE";
case SO_DONTROUTE: return "SO_DONTROUTE";
case SO_BROADCAST: return "SO_BROADCAST";
case SO_USELOOPBACK: return "SO_USELOOPBACK";
case SO_LINGER: return "SO_LINGER";
case SO_OOBINLINE: return "SO_OOBINLINE";
case SO_REUSEPORT: return "SO_REUSEPORT";
case SO_TIMESTAMP: return "SO_TIMESTAMP";
case SO_ONESBCAST: return "SO_ONESBCAST";
case SO_SNDBUF: return "SO_SNDBUF";
case SO_RCVBUF: return "SO_RCVBUF";
case SO_SNDLOWAT: return "SO_SNDLOWAT";
case SO_RCVLOWAT: return "SO_RCVLOWAT";
case SO_SNDTIMEO: return "SO_SNDTIMEO";
case SO_RCVTIMEO: return "SO_RCVTIMEO";
case SO_ERROR: return "SO_ERROR";
case SO_TYPE: return "SO_TYPE";
case SO_NONBLOCK: return "SO_NONBLOCK";
default: return String.format("Unknown option 0x%X", optionName);
}
}
public int checkSocket(int socket) {
if (!sockets.containsKey(socket)) {
log.warn(String.format("checkSocket invalid socket=0x%X", socket));
throw new SceKernelErrorException(-1); // Unknown error code
}
return socket;
}
public int checkAddressLength(int addressLength) {
if (addressLength < 16) {
log.warn(String.format("checkAddressLength invalid addressLength=%d", addressLength));
throw new SceKernelErrorException(-1); // Unknown error code
}
return addressLength;
}
private void getProxyForSockAddrInternet(pspNetSockAddrInternet sockAddrInternet) {
for (InetAddress inetAddress : doProxyInetAddresses) {
if (sockAddrInternet.equals(inetAddress)) {
sockAddrInternet.sin_addr = HTTPServer.getInstance().getProxyAddress();
sockAddrInternet.sin_port = HTTPServer.getInstance().getProxyPort();
break;
}
}
}
// int sceNetInetInit(void);
@HLEFunction(nid = 0x17943399, version = 150)
public int sceNetInetInit() {
return 0;
}
// int sceNetInetTerm(void);
@HLEFunction(nid = 0xA9ED66B9, version = 150)
public int sceNetInetTerm() {
return 0;
}
// int sceNetInetAccept(int s, struct sockaddr *addr, socklen_t *addrlen);
@HLEFunction(nid = 0xDB094E1B, version = 150)
public int sceNetInetAccept(@CheckArgument("checkSocket") int socket, TPointer address, TPointer32 addressLengthAddr) {
pspInetSocket inetSocket = sockets.get(socket);
pspNetSockAddrInternet sockAddrInternet = new pspNetSockAddrInternet();
int addressLength = addressLengthAddr.getValue();
// addressLength is unsigned int
if (addressLength < 0) {
addressLength = Integer.MAX_VALUE;
}
sockAddrInternet.setMaxSize(addressLength);
sockAddrInternet.write(address);
if (sockAddrInternet.sizeof() < addressLength) {
addressLengthAddr.setValue(sockAddrInternet.sizeof());
}
return inetSocket.accept(sockAddrInternet);
}
// int sceNetInetBind(int socket, const struct sockaddr *address, socklen_t address_len);
@HLEFunction(nid = 0x1A33F9AE, version = 150)
public int sceNetInetBind(@CheckArgument("checkSocket") int socket, pspNetSockAddrInternet sockAddrInternet, @CheckArgument("checkAddressLength") int addressLength) {
if (sockAddrInternet.sin_family != AF_INET) {
log.warn(String.format("sceNetInetBind invalid socket address family=%d", sockAddrInternet.sin_family));
return -1;
}
pspInetSocket inetSocket = sockets.get(socket);
int result = inetSocket.bind(sockAddrInternet);
if (result == 0) {
if (log.isInfoEnabled()) {
log.info(String.format("sceNetInetBind binding to %s", sockAddrInternet.toString()));
}
} else {
log.warn(String.format("sceNetInetBind failed binding to %s", sockAddrInternet.toString()));
}
return result;
}
// int sceNetInetClose(int s);
@HLEFunction(nid = 0x8D7284EA, version = 150)
public int sceNetInetClose(@CheckArgument("checkSocket") int socket) {
pspInetSocket inetSocket = sockets.get(socket);
int result = inetSocket.close();
releaseSocketId(socket);
sockets.remove(socket);
return result;
}
@HLEUnimplemented
@HLEFunction(nid = 0x805502DD, version = 150)
public int sceNetInetCloseWithRST() {
return 0;
}
// int sceNetInetConnect(int socket, const struct sockaddr *serv_addr, socklen_t addrlen);
@HLEFunction(nid = 0x410B34AA, version = 150)
public int sceNetInetConnect(@CheckArgument("checkSocket") int socket, pspNetSockAddrInternet sockAddrInternet, @CheckArgument("checkAddressLength") int addressLength) {
if (sockAddrInternet.sin_family != AF_INET) {
log.warn(String.format("sceNetInetConnect invalid socket address family=%d", sockAddrInternet.sin_family));
return -1;
}
getProxyForSockAddrInternet(sockAddrInternet);
pspInetSocket inetSocket = sockets.get(socket);
int result = inetSocket.connect(sockAddrInternet);
if (result == 0) {
if (log.isInfoEnabled()) {
log.info(String.format("sceNetInetConnect connected to %s", sockAddrInternet.toString()));
}
} else {
if (getErrno() == EINPROGRESS) {
if (log.isInfoEnabled()) {
log.info(String.format("sceNetInetConnect connecting to %s", sockAddrInternet.toString()));
}
} else {
log.warn(String.format("sceNetInetConnect failed connecting to %s (errno=%d)", sockAddrInternet.toString(), getErrno()));
}
}
return result;
}
@HLEFunction(nid = 0xE247B6D6, version = 150)
public int sceNetInetGetpeername(@CheckArgument("checkSocket") int socket, TPointer address, TPointer32 addressLengthAddr) {
pspNetSockAddrInternet sockAddrInternet = new pspNetSockAddrInternet();
pspInetSocket inetSocket = sockets.get(socket);
int result = inetSocket.getSockname(sockAddrInternet);
sockAddrInternet.setMaxSize(addressLengthAddr.getValue());
sockAddrInternet.write(address);
addressLengthAddr.setValue(sockAddrInternet.sizeof());
return result;
}
@HLEFunction(nid = 0x162E6FD5, version = 150)
public int sceNetInetGetsockname(@CheckArgument("checkSocket") int socket, TPointer address, TPointer32 addressLengthAddr) {
pspNetSockAddrInternet sockAddrInternet = new pspNetSockAddrInternet();
pspInetSocket inetSocket = sockets.get(socket);
int result = inetSocket.getSockname(sockAddrInternet);
sockAddrInternet.setMaxSize(addressLengthAddr.getValue());
sockAddrInternet.write(address);
addressLengthAddr.setValue(sockAddrInternet.sizeof());
return result;
}
@HLEFunction(nid = 0x4A114C7C, version = 150)
public int sceNetInetGetsockopt(@CheckArgument("checkSocket") int socket, int level, int optionName, TPointer optionValue, @CanBeNull TPointer32 optionLengthAddr) {
int optionLength = optionLengthAddr.isNull() ? 0 : optionLengthAddr.getValue();
if (log.isDebugEnabled()) {
log.debug(String.format("sceNetInetGetsockopt optionName=%s, optionLength=%d", getOptionNameString(optionName), optionLength));
}
pspInetSocket inetSocket = sockets.get(socket);
if (optionName == SO_ERROR && optionLength >= 4) {
optionValue.setValue32(inetSocket.getErrorAndClearToSelf());
if (log.isDebugEnabled()) {
log.debug(String.format("sceNetInetGetsockopt SO_ERROR returning %d", optionValue.getValue32()));
}
optionLengthAddr.setValue(4);
} else if (optionName == SO_NONBLOCK && optionLength >= 4) {
optionValue.setValue32(inetSocket.isBlocking());
optionLengthAddr.setValue(4);
} else if (optionName == SO_BROADCAST && optionLength >= 4) {
optionValue.setValue32(inetSocket.isBroadcast());
optionLengthAddr.setValue(4);
} else if (optionName == SO_RCVLOWAT && optionLength >= 4) {
optionValue.setValue32(inetSocket.getReceiveLowWaterMark());
optionLengthAddr.setValue(4);
} else if (optionName == SO_SNDLOWAT && optionLength >= 4) {
optionValue.setValue32(inetSocket.getSendLowWaterMark());
optionLengthAddr.setValue(4);
} else if (optionName == SO_RCVTIMEO && optionLength >= 4) {
int timeout = inetSocket.getReceiveTimeout();
// Returning 0 for "no timeout" value
optionValue.setValue32(timeout == pspInetSocket.NO_TIMEOUT_INT ? 0 : timeout);
optionLengthAddr.setValue(4);
} else if (optionName == SO_SNDTIMEO && optionLength >= 4) {
int timeout = inetSocket.getSendTimeout();
// Returning 0 for "no timeout" value
optionValue.setValue32(timeout == pspInetSocket.NO_TIMEOUT_INT ? 0 : timeout);
optionLengthAddr.setValue(4);
} else if (optionName == SO_RCVBUF && optionLength >= 4) {
optionValue.setValue32(inetSocket.getReceiveBufferSize());
optionLengthAddr.setValue(4);
} else if (optionName == SO_SNDBUF && optionLength >= 4) {
optionValue.setValue32(inetSocket.getSendBufferSize());
optionLengthAddr.setValue(4);
} else if (optionName == SO_KEEPALIVE && optionLength >= 4) {
optionValue.setValue32(inetSocket.isKeepAlive());
optionLengthAddr.setValue(4);
} else if (optionName == SO_LINGER && optionLength >= 8) {
optionValue.setValue32(0, inetSocket.isLingerEnabled());
optionValue.setValue32(4, inetSocket.getLinger());
optionLengthAddr.setValue(8);
} else if (optionName == SO_REUSEADDR && optionLength >= 4) {
optionValue.setValue32(inetSocket.isReuseAddress());
optionLengthAddr.setValue(4);
} else {
log.warn(String.format("Unimplemented sceNetInetGetsockopt socket=0x%X, level=0x%X, optionName=0x%X, optionValue=%s, optionLength=%s(0x%X)", socket, level, optionName, optionValue, optionLengthAddr, optionLength));
return -1;
}
return 0;
}
// int sceNetInetListen(int s, int backlog);
@HLEFunction(nid = 0xD10A1A7A, version = 150)
public int sceNetInetListen(@CheckArgument("checkSocket") int socket, int backlog) {
pspInetSocket inetSocket = sockets.get(socket);
return inetSocket.listen(backlog);
}
/*
* sceNetInetPoll seems to work in a similar way to the BSD socket poll() function:
*
* int poll(struct pollfd *fds, nfds_t nfds, int timeout);
* fds Points to an array of pollfd structures, which are defined as:
* struct pollfd {
* int fd;
* short events;
* short revents;
* }
* The fd member is an open file descriptor. If fd is -1, the
* pollfd structure is considered unused, and revents will be
* cleared.
*
* The events and revents members are bitmasks of conditions to
* monitor and conditions found, respectively.
*
* nfds An unsigned integer specifying the number of pollfd structures
* in the array.
*
* timeout Maximum interval to wait for the poll to complete, in milliseconds.
* If this value is 0, poll() will return immediately.
* If this value is INFTIM (-1), poll() will block indefinitely until
* a condition is found.
*
* The calling process sets the events bitmask and poll() sets the revents
* bitmask. Each call to poll() resets the revents bitmask for accuracy. The
* condition flags in the bitmasks are defined as:
*
* POLLIN Data other than high-priority data may be read without blocking.
* POLLRDNORM Normal data may be read without blocking.
* POLLRDBAND Priority data may be read without blocking.
* POLLPRI High-priority data may be read without blocking.
* POLLOUT Normal data may be written without blocking.
* POLLWRBAND Priority data may be written.
* POLLERR An error has occurred on the device or socket. This flag is
* only valid in the revents bitmask; it is ignored in the
* events member.
* POLLHUP The device or socket has been disconnected. This event and
* POLLOUT are mutually-exclusive; a descriptor can never be
* writable if a hangup has occurred. However, this event and
* POLLIN, POLLRDNORM, POLLRDBAND, or POLLPRI are not mutually-
* exclusive. This flag is only valid in the revents bitmask; it
* is ignored in the events member.
* POLLNVAL The corresponding file descriptor is invalid. This flag is
* only valid in the revents bitmask; it is ignored in the
* events member.
*
* Bitmask Values:
* POLLIN 0x0001
* POLLRDNORM 0x0040
* POLLRDBAND 0x0080
* POLLPRI 0x0002
* POLLOUT 0x0004
* POLLWRBAND 0x0100
* POLLERR 0x0008
* POLLHUP 0x0010
* POLLNVAL 0x0020
*
* Return values:
* Upon error, poll() returns -1 and sets the global variable errno
* to indicate the error. If the timeout interval was reached before
* any events occurred, poll() returns 0. Otherwise, poll() returns
* the number of file descriptors for which revents is non-zero.
*/
@HLEFunction(nid = 0xFAABB1DD, version = 150)
public int sceNetInetPoll(TPointer fds, int nfds, int timeout) {
int result = 0;
long timeoutUsec;
if (timeout == POLL_INFTIM) {
timeoutUsec = pspInetSocket.NO_TIMEOUT;
} else {
timeoutUsec = timeout * 1000L;
}
try {
Selector selector = Selector.open();
pspInetPollFd[] pollFds = new pspInetPollFd[nfds];
for (int i = 0; i < nfds; i++) {
pspInetPollFd pollFd = new pspInetPollFd();
pollFd.read(fds, i * pollFd.sizeof());
pollFds[i] = pollFd;
if (pollFd.fd == -1) {
pollFd.revents = 0;
} else {
pspInetSocket inetSocket = sockets.get(pollFd.fd);
if (inetSocket == null) {
pollFd.revents = POLLNVAL;
} else {
SelectableChannel selectableChannel = inetSocket.getSelectableChannel();
if (selectableChannel == null) {
pollFd.revents = POLLHUP;
} else {
int registeredOperations = 0;
if ((pollFd.events & (POLLIN | POLLRDNORM | POLLRDBAND | POLLPRI)) != 0) {
registeredOperations |= SelectionKey.OP_READ;
}
if ((pollFd.events & (POLLOUT | POLLWRBAND)) != 0) {
registeredOperations |= SelectionKey.OP_WRITE;
}
registeredOperations &= selectableChannel.validOps();
if (selectableChannel.isRegistered()) {
log.warn(String.format("sceNetInetPoll channel already registered pollFd[%d]=%s", i, pollFd));
} else {
try {
selectableChannel.register(selector, registeredOperations, new Integer(pollFd.fd));
pollFd.revents = 0;
} catch (ClosedChannelException e) {
pollFd.revents = POLLHUP;
}
}
}
}
}
if (log.isDebugEnabled()) {
log.debug(String.format("sceNetInetPoll pollFd[%d]=%s", i, pollFd));
}
}
BlockingPollState blockingState = new BlockingPollState(selector, pollFds, timeoutUsec);
SceKernelThreadInfo thread = Modules.ThreadManForUserModule.getCurrentThread();
thread.cpuContext._v0 = 0; // This will be overwritten by the execution of the blockingState
setErrno(0, blockingState);
// Check if there are ready operations, otherwise, block the thread
blockingState.execute();
result = thread.cpuContext._v0;
} catch (IOException e) {
log.error("sceNetInetPoll", e);
setErrnoToSelf(-1);
return -1;
}
return result;
}
// size_t sceNetInetRecv(int s, void *buf, size_t len, int flags);
@HLEFunction(nid = 0xCDA85C99, version = 150)
public int sceNetInetRecv(@CheckArgument("checkSocket") int socket, @BufferInfo(lengthInfo=LengthInfo.returnValue, usage=Usage.out) TPointer buffer, int bufferLength, int flags) {
pspInetSocket inetSocket = sockets.get(socket);
return inetSocket.recv(buffer.getAddress(), bufferLength, flags);
}
// size_t sceNetInetRecvfrom(int socket, void *buffer, size_t bufferLength, int flags, struct sockaddr *from, socklen_t *fromlen);
@HLEFunction(nid = 0xC91142E4, version = 150)
public int sceNetInetRecvfrom(@CheckArgument("checkSocket") int socket, @BufferInfo(lengthInfo=LengthInfo.returnValue, usage=Usage.out) TPointer buffer, int bufferLength, int flags, TPointer from, TPointer32 fromLengthAddr) {
pspInetSocket inetSocket = sockets.get(socket);
pspNetSockAddrInternet fromAddrInternet = new pspNetSockAddrInternet();
int fromLength = fromLengthAddr.getValue();
fromAddrInternet.setMaxSize(fromLength);
fromAddrInternet.write(from);
if (fromAddrInternet.sizeof() < fromLength) {
fromLengthAddr.setValue(fromAddrInternet.sizeof());
}
return inetSocket.recvfrom(buffer.getAddress(), bufferLength, flags, fromAddrInternet);
}
@HLEUnimplemented
@HLEFunction(nid = 0xEECE61D2, version = 150)
public int sceNetInetRecvmsg() {
return 0;
}
@HLEFunction(nid = 0x5BE8D595, version = 150)
public int sceNetInetSelect(int numberSockets, @CanBeNull TPointer readSocketsAddr, @CanBeNull TPointer writeSocketsAddr, @CanBeNull TPointer outOfBandSocketsAddr, @CanBeNull TPointer32 timeoutAddr) {
int result = 0;
numberSockets = Math.min(numberSockets, 256);
long timeoutUsec;
if (timeoutAddr.isNotNull()) {
// timeoutAddr points to the following structure:
// - offset 0: int seconds
// - offset 4: int microseconds
timeoutUsec = timeoutAddr.getValue(0) * 1000000L;
timeoutUsec += timeoutAddr.getValue(4);
} else {
// Take a very large value
timeoutUsec = pspInetSocket.NO_TIMEOUT;
}
if (log.isDebugEnabled()) {
log.debug(String.format("sceNetInetSelect timeout=%d us", timeoutUsec));
if (readSocketsAddr.isNotNull()) {
log.debug(String.format("sceNetInetSelect Read Sockets : %s", dumpSelectBits(readSocketsAddr, numberSockets)));
}
if (writeSocketsAddr.isNotNull()) {
log.debug(String.format("sceNetInetSelect Write Sockets : %s", dumpSelectBits(writeSocketsAddr, numberSockets)));
}
if (outOfBandSocketsAddr.isNotNull()) {
log.debug(String.format("sceNetInetSelect Out-of-band Sockets: %s", dumpSelectBits(outOfBandSocketsAddr, numberSockets)));
}
}
try {
Selector selector = Selector.open();
RawSelector rawSelector = RawSelector.open();
int count = 0;
// Read the socket list for the read operation and register them with the selector
count += readSocketList(selector, rawSelector, readSocketsAddr, numberSockets, readSelectionKeyOperations, "readSockets");
// Read the socket list for the write operation and register them with the selector
count += readSocketList(selector, rawSelector, writeSocketsAddr, numberSockets, writeSelectionKeyOperations, "writeSockets");
// Read the socket list for the out-of-band data and register them with the selector.
// Out-of-band data is currently not implemented as I don't see any
// support in Java for this rarely used feature.
count += readSocketList(selector, rawSelector, outOfBandSocketsAddr, numberSockets, 0, "outOfBandSockets");
BlockingSelectState blockingState = new BlockingSelectState(selector, rawSelector, numberSockets, readSocketsAddr, writeSocketsAddr, outOfBandSocketsAddr, timeoutUsec, count);
setErrno(0, blockingState);
SceKernelThreadInfo thread = Modules.ThreadManForUserModule.getCurrentThread();
thread.cpuContext._v0 = 0; // This will be overwritten by the execution of the blockingState
// Check if there are ready operations, otherwise, block the thread
blockingState.execute();
result = thread.cpuContext._v0;
} catch (IOException e) {
log.error(e);
setErrnoToSelf(-1);
return -1;
}
return result;
}
// size_t sceNetInetSend(int socket, const void *buffer, size_t bufferLength, int flags);
@HLEFunction(nid = 0x7AA671BC, version = 150)
public int sceNetInetSend(@CheckArgument("checkSocket") int socket, @BufferInfo(lengthInfo=LengthInfo.nextParameter, usage=Usage.in) TPointer buffer, int bufferLength, int flags) {
if (log.isTraceEnabled()) {
log.trace(String.format("Send data: %s", Utilities.getMemoryDump(buffer.getAddress(), bufferLength)));
}
pspInetSocket inetSocket = sockets.get(socket);
return inetSocket.send(buffer.getAddress(), bufferLength, flags);
}
// size_t sceNetInetSendto(int socket, const void *buffer, size_t bufferLength, int flags, const struct sockaddr *to, socklen_t tolen);
@HLEFunction(nid = 0x05038FC7, version = 150)
public int sceNetInetSendto(@CheckArgument("checkSocket") int socket, @BufferInfo(lengthInfo=LengthInfo.nextParameter, usage=Usage.in) TPointer buffer, int bufferLength, int flags, pspNetSockAddrInternet toSockAddress, @CheckArgument("checkAddressLength") int toLength) {
if (log.isTraceEnabled()) {
log.trace(String.format("Sendto data: %s", Utilities.getMemoryDump(buffer.getAddress(), bufferLength)));
}
if (toSockAddress.sin_family != AF_INET) {
log.warn(String.format("sceNetInetSendto invalid socket address familiy sin_family=%d", toSockAddress.sin_family));
return -1;
}
pspInetSocket inetSocket = sockets.get(socket);
return inetSocket.sendto(buffer.getAddress(), bufferLength, flags, toSockAddress);
}
@HLEUnimplemented
@HLEFunction(nid = 0x774E36F4, version = 150)
public int sceNetInetSendmsg() {
return 0;
}
// int sceNetInetSetsockopt(int socket, int level, int optname, const void *optval, socklen_t optlen);
@HLEFunction(nid = 0x2FE71FE7, version = 150)
public int sceNetInetSetsockopt(@CheckArgument("checkSocket") int socket, int level, int optionName, @CanBeNull TPointer optionValueAddr, int optionLength) {
int result = 0;
int optionValue = 0;
if (optionValueAddr.isNotNull() && optionLength >= 4) {
optionValue = optionValueAddr.getValue32();
}
if (log.isDebugEnabled()) {
log.debug(String.format("sceNetInetSetsockopt optionName=%s", getOptionNameString(optionName)));
if (log.isTraceEnabled()) {
log.trace(String.format("Option value: %s", Utilities.getMemoryDump(optionValueAddr.getAddress(), optionLength)));
}
}
pspInetSocket inetSocket = sockets.get(socket);
if (level == SOL_SOCKET) {
if (optionName == SO_NONBLOCK && optionLength == 4) {
result = inetSocket.setBlocking(optionValue == 0);
} else if (optionName == SO_BROADCAST && optionLength == 4) {
result = inetSocket.setBroadcast(optionValue != 0);
} else if (optionName == SO_RCVLOWAT && optionLength == 4) {
inetSocket.setReceiveLowWaterMark(optionValue);
result = 0;
} else if (optionName == SO_SNDLOWAT && optionLength == 4) {
inetSocket.setSendLowWaterMark(optionValue);
result = 0;
} else if (optionName == SO_RCVTIMEO && optionLength == 4) {
// 0 means "no timeout"
inetSocket.setReceiveTimeout(optionValue == 0 ? pspInetSocket.NO_TIMEOUT_INT : optionValue);
result = 0;
} else if (optionName == SO_SNDTIMEO && optionLength == 4) {
// 0 means "no timeout"
inetSocket.setSendTimeout(optionValue == 0 ? pspInetSocket.NO_TIMEOUT_INT : optionValue);
result = 0;
} else if (optionName == SO_RCVBUF && optionLength == 4) {
result = inetSocket.setReceiveBufferSize(optionValue);
} else if (optionName == SO_SNDBUF && optionLength == 4) {
result = inetSocket.setSendBufferSize(optionValue);
} else if (optionName == SO_KEEPALIVE && optionLength == 4) {
result = inetSocket.setKeepAlive(optionValue != 0);
} else if (optionName == SO_LINGER && optionLength == 8) {
result = inetSocket.setLinger(optionValue != 0, optionValueAddr.getValue32(4));
} else if (optionName == SO_REUSEADDR && optionLength == 4) {
result = inetSocket.setReuseAddress(optionValue != 0);
} else {
log.warn(String.format("Unimplemented sceNetInetSetsockopt optionName=%s", getOptionNameString(optionName)));
result = 0;
}
} else {
log.warn(String.format("Unimplemented sceNetInetSetsockopt unknown level=0x%X, optionName=%s", level, getOptionNameString(optionName)));
result = 0;
}
return result;
}
// int sceNetInetShutdown(int s, int how);
@HLEFunction(nid = 0x4CFE4E56, version = 150)
public int sceNetInetShutdown(@CheckArgument("checkSocket") int socket, int how) {
if (how < SHUT_RD || how > SHUT_RDWR) {
log.warn(String.format("sceNetInetShutdown invalid how=%d", how));
return -1;
}
pspInetSocket inetSocket = sockets.get(socket);
return inetSocket.shutdown(how);
}
// int sceNetInetSocket(int domain, int type, int protocol);
@HLEFunction(nid = 0x8B7B220F, version = 150)
public int sceNetInetSocket(int domain, int type, int protocol) {
if (log.isDebugEnabled()) {
log.debug(String.format("sceNetInetSocket domain=0x%X, type=0x%X(%s), protocol=0x%X", domain, type, getSocketTypeNameString(type), protocol));
}
if (domain != AF_INET) {
log.warn(String.format("sceNetInetSocket unsupported domain=0x%X, type=0x%X(%s), protocol=0x%X", domain, type, getSocketTypeNameString(type), protocol));
return -1;
}
if (type != SOCK_DGRAM && type != SOCK_STREAM && type != SOCK_RAW && type != SOCK_DGRAM_UNKNOWN_6 && type != SOCK_STREAM_UNKNOWN_10) {
log.warn(String.format("sceNetInetSocket unsupported type=0x%X(%s), domain=0x%X, protocol=0x%X", type, getSocketTypeNameString(type), domain, protocol));
return -1;
}
pspInetSocket inetSocket = createSocket(type, protocol);
if (log.isDebugEnabled()) {
log.debug(String.format("sceNetInetSocket created socket=0x%X", inetSocket.getUid()));
}
return inetSocket.getUid();
}
@HLEUnimplemented
@HLEFunction(nid = 0x80A21ABD, version = 150)
public int sceNetInetSocketAbort(@CheckArgument("checkSocket") int socket) {
return 0;
}
@HLEFunction(nid = 0xFBABE411, version = 150)
public int sceNetInetGetErrno() {
if (log.isDebugEnabled()) {
log.debug(String.format("sceNetInetGetErrno returning 0x%08X(%1$d)", getErrno()));
}
return getErrno();
}
@HLEUnimplemented
@HLEFunction(nid = 0xB3888AD4, version = 150)
public int sceNetInetGetTcpcbstat(TPointer32 sizeAddr, @DebugMemory @CanBeNull @BufferInfo(lengthInfo=LengthInfo.fixedLength, length=26, usage=Usage.out) TPointer buf) {
int tcpCount = 0;
for (pspInetSocket socket : sockets.values()) {
if (socket instanceof pspInetStreamSocket) {
tcpCount++;
}
}
int size = sizeAddr.getValue();
SceNetInetTcpcbstat stat = new SceNetInetTcpcbstat();
sizeAddr.setValue(stat.sizeof() * tcpCount);
if (log.isDebugEnabled()) {
log.debug(String.format("sceNetInetGetTcpcbstat returning size=%d", sizeAddr.getValue()));
}
if (buf.isNotNull()) {
int offset = 0;
for (pspInetSocket socket : sockets.values()) {
if (socket instanceof pspInetStreamSocket) {
// Check if enough space available to write the next structure
if (offset + stat.sizeof() > size || socket == null) {
break;
}
if (log.isDebugEnabled()) {
log.debug(String.format("sceNetInetGetTcpcbstat returning %s at 0x%08X", socket, buf.getAddress() + offset));
}
stat.ts_so_snd_sb_cc = 0;
stat.ts_so_rcv_sb_cc = 0;
pspNetSockAddrInternet localAddr = socket.getLocalAddr();
if (localAddr == null) {
stat.ts_inp_laddr = 0;
stat.ts_inp_lport = 0;
} else {
stat.ts_inp_laddr = localAddr.sin_addr;
stat.ts_inp_lport = localAddr.sin_port;
}
pspNetSockAddrInternet remoteAddr = socket.getRemoteAddr();
if (remoteAddr == null) {
stat.ts_inp_faddr = 0;
stat.ts_inp_fport = 0;
} else {
stat.ts_inp_faddr = remoteAddr.sin_addr;
stat.ts_inp_fport = remoteAddr.sin_port;
}
// TODO Find the correct socket state
stat.ts_t_state = TCPS_ESTABLISHED;
stat.write(buf, offset);
offset += stat.sizeof();
}
}
fillNextPointersInLinkedList(buf, offset, stat.sizeof());
if (log.isDebugEnabled()) {
log.debug(String.format("sceNetInetGetTcpcbstat returning %s", Utilities.getMemoryDump(buf.getAddress(), offset)));
}
}
return 0;
}
@HLEUnimplemented
@HLEFunction(nid = 0x39B0C7D3, version = 150)
public int sceNetInetGetUdpcbstat() {
return 0;
}
@HLEFunction(nid = 0xB75D5B0A, version = 150)
public int sceNetInetInetAddr(PspString name) {
byte[] inetAddressBytes;
try {
inetAddressBytes = InetAddress.getByName(name.getString()).getAddress();
} catch (UnknownHostException e) {
log.error("sceNetInetInetAddr", e);
return -1;
}
int inetAddress = bytesToInternetAddress(inetAddressBytes);
if (log.isDebugEnabled()) {
log.debug(String.format("sceNetInetInetAddr %s returning 0x%08X", name, inetAddress));
}
return inetAddress;
}
@HLEFunction(nid = 0x1BDF5D13, version = 150)
public int sceNetInetInetAton(PspString hostname, TPointer32 addr) {
int result;
try {
InetAddress inetAddress = InetAddress.getByName(hostname.getString());
int resolvedAddress = bytesToInternetAddress(inetAddress.getAddress());
addr.setValue(resolvedAddress);
if (log.isDebugEnabled()) {
log.debug(String.format("sceNetInetInetAton returning address 0x%08X('%s')", resolvedAddress, sceNetInet.internetAddressToString(resolvedAddress)));
} else if (log.isInfoEnabled()) {
log.info(String.format("sceNetInetInetAton resolved '%s' into '%s'", hostname.getString(), sceNetInet.internetAddressToString(resolvedAddress)));
}
result = 1;
} catch (UnknownHostException e) {
log.error("sceNetInetInetAton", e);
result = 0;
}
return result;
}
@HLEFunction(nid = 0xD0792666, version = 150)
public int sceNetInetInetNtop(int family, TPointer32 srcAddr, TPointer buffer, int bufferLength) {
if (family != AF_INET) {
log.warn(String.format("sceNetInetInetNtop unsupported family 0x%X", family));
return 0;
}
int addr = srcAddr.getValue();
String ip = internetAddressToString(addr);
if (log.isDebugEnabled()) {
log.debug(String.format("sceNetInetInetNtop returning %s for 0x%08X", ip, addr));
}
buffer.setStringNZ(bufferLength, ip);
return buffer.getAddress();
}
@HLEFunction(nid = 0xE30B8C19, version = 150)
public int sceNetInetInetPton(int family, PspString src, TPointer32 buffer) {
int result;
if (family != AF_INET) {
log.warn(String.format("sceNetInetInetPton unsupported family 0x%X", family));
return -1;
}
try {
byte[] inetAddressBytes = InetAddress.getByName(src.getString()).getAddress();
int inetAddress = bytesToInternetAddress(inetAddressBytes);
buffer.setValue(inetAddress);
if (log.isDebugEnabled()) {
log.debug(String.format("sceNetInetInetPton returning 0x%08X for '%s'", inetAddress, src.getString()));
}
result = 1;
} catch (UnknownHostException e) {
log.warn(String.format("sceNetInetInetPton returned error '%s' for '%s'", e.toString(), src.getString()));
result = 0;
}
return result;
}
@HLEFunction(nid = 0x8CA3A97E, version = 150)
public int sceNetInetGetPspError() {
if (log.isDebugEnabled()) {
log.debug(String.format("sceNetInetGetPspError returning 0x%08X(%1$d)", getErrno()));
}
return ERROR_ERRNO_BASE | (getErrno() & 0xFFFF);
}
@HLEUnimplemented
@HLEFunction(nid = 0xAEE60F84, version = 150)
public int sceNetInet_lib_AEE60F84(@CheckArgument("checkSocket") int socket, int unknown1, int unknown2) {
return 0;
}
}